Python网络编程07----Django中的URLconf

介绍

    URLconf 就像是 Django 所支撑网站的目录。 它的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。 利用Django开发网站,可以设计出非常优美的url规则,如果url的匹配规则(包含正则表达式)组织得比较好,view的结构就会比较清晰,比较容易维护。

基本形式

URLconf存放的映射关系在Python文件里,这个文件中必须暴露出一个urlpatterns对象,这个对象则应该是Django定义patterns函数的结果。这个函数由以下两部分组成:
@前缀字符串,可以为空。(如果第一个参数非空的话,就会被加到函数字符串之前)
@一个或若干url方法,他接受三个参数:正则表达式、视图函数对象或字符串和一个可选字典参数.。(以前的老版本是一个或若干元组,元组包含正则表达式、视图函数对象或字符串、可选的视图函数字典参数)

带url方法的形式
[python]  view plain copy
  
  1. from django.conf.urls import patterns, url  
  2. urlpatterns = patterns('',  
  3.     url(r'^articles/2003/$''news.views.special_case_2003'),  
  4.     url(r'^articles/(\d{4})/$''news.views.year_archive'),  
  5.     url(r'^articles/(\d{4})/(\d{2})/$''news.views.month_archive'),  
  6.     url(r'^articles/(\d{4})/(\d{2})/(\d+)/$''news.views.article_detail'),  
  7. )
对应的元组的形式
[python]  view plain copy
  
  1. from django.conf.urls import patterns, url  
  2. urlpatterns = patterns('',  
  3.     (r'^articles/2003/$''news.views.special_case_2003'),  
  4.     (r'^articles/(\d{4})/$''news.views.year_archive'),  
  5.     (r'^articles/(\d{4})/(\d{2})/$''news.views.month_archive'),  
  6.     (r'^articles/(\d{4})/(\d{2})/(\d+)/$''news.views.article_detail'),  
  7. )
在新版的django还可以这样写
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
]


注意:
  • 域名部分会被过滤掉
  • articles的前面不需要添加/,因为域名的末尾一定会有/
  • 任何组匹配的变量,都会以字符串的形式传递给view, 虽然通过(\d{4})匹配出了2005,但2005任然会被当做字符串传递给year_archive

利用named group来传递参数

可以通过以下形式为特定的组指定一个名称.
注意下面的?P<year>
[python]  view plain copy
  1. urlpatterns = patterns('',  
  2.     url(r'^articles/2003/$''news.views.special_case_2003'),  
  3.     url(r'^articles/(?P<year>\d{4})/$''news.views.year_archive'),   #year_archive(request,year)中的形参必须是year才能获取参数
  4.     url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$''news.views.month_archive'),  
  5.     url(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$''news.views.article_detail'),  
  6. )  
这样的话,组的匹配结果会通过keyword parameters的形式传递给view.例如 year_archive(year='2005')
利用named group可以为view指定一个默认参数来匹配多条规则。
[python]  view plain copy
  1. # URLconf  
  2. from django.conf.urls import patterns, url  
  3.   
  4. urlpatterns = patterns('',  
  5.     url(r'^blog/$''blog.views.page'),  
  6.     url(r'^blog/page(?P<num>\d+)/$''blog.views.page'),  
  7. )  
  8.   
  9. # View (in blog/views.py)  
  10. def page(request, num="1"):  
  11.     # Output the appropriate page of blog entries, according to num.  

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', index),
    url(r'^list/(?P<name>\S*)/(?P<id>\d*)/$', list),
    url(r'^list/(?P<name>\S*)/$', list,{"id":123}),   #如果不提供第二个参数,则自动输入123
]


指定view前缀(提取公因式)

patterns函数的第一个参数即是view的前缀.
[python]  view plain copy
  1. from django.conf.urls import patterns, url  
  2.   
  3. urlpatterns = patterns('news.views',  
  4.     url(r'^articles/(\d{4})/$''year_archive'),  
  5.     url(r'^articles/(\d{4})/(\d{2})/$''month_archive'),  
  6.     url(r'^articles/(\d{4})/(\d{2})/(\d+)/$''article_detail'),  
  7. )  

指定多个view前缀

[python]  view plain copy
  1. urlpatterns = patterns('myapp.views',  
  2.     url(r'^$''app_index'),  
  3.     url(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','month_display'),  
  4. )  
  5. urlpatterns += patterns('weblog.views',       #注意使用+=而不是=
  6.     url(r'^tag/(?P<tag>\w+)/$''tag'),  
  7. )  

include其它匹配模块

通过include优化url规则,更加容易维护。
[python]  view plain copy
  1. from django.conf.urls import include, patterns, url  
  2.   
  3. urlpatterns = patterns('',  
  4.     # ... snip ...  
  5.     url(r'^comments/', include('django.contrib.comments.urls')),      
  6.     url(r'^community/', include('django_website.aggregator.urls')),  
  7.     url(r'^contact/', include('django_website.contact.urls')),  
  8.     # ... snip ...  
  9. )  
当然也可以直接include其它patterns
[python]  view plain copy
  1. from django.conf.urls import include, patterns, url  
  2.   
  3. extra_patterns = patterns('',  
  4.     url(r'^reports/(?P<id>\d+)/$''credit.views.report'),  
  5.     url(r'^charge/$''credit.views.charge'),  
  6. )  
  7.   
  8. urlpatterns = patterns('',                                  
  9.     url(r'^$''apps.main.views.homepage'),  
  10.     url(r'^help/', include('apps.help.urls')),  
  11.     url(r'^credit/', include(extra_patterns)),  
  12. )  

实例

动态内容

       我们做一个显示当前日期和时间的页面。这个例子足够简单, 不涉及数据库或者用户输入,仅仅是将服务器上的时间显示到页面上。

       这个视图需要做两件事:计算当前的日期和时间,以及返回一个包含这个值的 HttpResponse。如果你有过Python的经验,你也许知道Python有一个 datetime 模块可以用来计算日期。 下面我们来看看如何用它:

>>> import datetime
>>> print(datetime.datetime.now())
2015-01-27 09:54:03.891556
      很简单,也并没有涉及到Django。它仅仅是Python的代码。(我们希望你注意哪些是纯Python代码,哪些 是有Django特性的代码,这样,在你学习Django的过程中,你也可以学到一些Python的知识,并用到其他的 不用Django的Python项目中。)

      要想我们的Django视图显示当前的日期和时间,我们仅需把语句 datetime.datetime.now() 放到 视图函数里面,然后返回一个 HttpResponse 。代码如下:

import datetime
from django.http import HttpResponse

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

如果没有必要,我们就不再重复列出先前的代码。你应该能识别出哪些是先前的代码,哪些是新的代码。

我们来回顾一下刚刚添加 current_datetime 时做的更改。

  • 首先,我们在文件顶端添加了一条语句 import datetime , 这样我们就可以计算日期了。

  • current_datetime 函数计算当前的日期和时间,以 datetime.datetime 的形式 保存在 now 这个局部变量中。

  • 函数的第二行,我们用Python的格式化字符串(format-string)构造了一段HTML。 字符串中的 %s 是一个占位符,字符串后面的百分号(%)表示用变量 now 的值代替前面 字符串中的 %s 。now 是一个 datetime.datetime 而不是一个字符 串, %s (格式化字符)会把它转换 "2014-11-27 17:28:31.002425" 这样的字符串表现形式。所以,最后会输出 "It is now 2014-11-27 17:28:31.002425." 这样的HTML字符串。

  • 我们现在的HTML是有错误的,我们这样做是为了保持例子的简短。

  • 最后,我们的视图和刚刚的 hello 视图一样返回一个 HttpResponse 对象,包含了刚刚生成的响应。

在 views.py 中添加视图之后,我们还要在 urls.py 中添加URLpattern来告诉Django由哪个URL来处理 这个视图。用 /time 之类的字眼会比较容易理解:

from django.conf.urls.defaults import patterns, include, url
from mysite.views import hello, current_datetime

urlpatterns = patterns('',
    url(r'^hello/$', hello),
    url(r'^time/$', current_datetime),
)

这里,我们修改了两个地方,首先,在顶部导入了 current_datetime 函数。 然后,更重要的一步是我们 添加了一个URL /time 关联到这个新视图。理解了吗?

写好了视图,添加了URLpattern,现在运行 runserver 并访问在浏览器里面访问 http://127.0.0.1:8000/time/ ,你将会看到当前的时间和日期。

动态URL

    在我们的上一个视图 current_datetime ,尽管内容是动态的,但是URL (/time/) 是静态的。 在大多数动态的Web应用程序中,URL通常可以包含一些参数可以控制页面的输出。比如一个在线书店会 给每本书分配一个URL,如 /books/243/ , /books/81196/ 这样。

    那让我们来创建下一个个视图,显示当前时间加上一个偏移量的时间。我们的目标是 /time/plus/1/ 显示当前时间+1个小时的页面, /time/plus/2/ 显示当前时间+2个小时的 页面, /time/plus/3/ 显示+3个小时的页面,以此类推。

关于漂亮URL的一点建议

如果你有其他Web平台的开发经验(比如PHP或Java), 你可能会想:嘿!让我们用查询字符串参数 吧!像 /time/plus?hours=3 这样,里面的小时应该在查询字符串中被参数 hours 指定(问号后面的部分)。

你可以这样做,但是Django的一个核心理念是URL看起来必须漂亮。像 /time/plus/3/ 这样的URL更清晰,更简单,也更具可读性,可以很容易被大声念出来。 漂亮的URL就是高质量的Web应用的一个标志。

那么,我们要怎样来处理任意小时的偏差呢?我们要用到通配符。我们前面有提到,一个URL模式就是一 个正则表达式,因此,我们可以在这里用\d+来匹配一个以上的数字。

urlpatterns = patterns('',
    # ...
    url(r'^time/plus/\d+/$', hours_ahead),
    # ...
)

这个URLpattern可以匹配任意类似 /time/plus/2/ , /time/plus/25/ 甚至是 /time/plus/100000000000/ 这样的URL。更进一步,让我们把它限制在最大允许99个小时,这样 我们就只允许一个或两个数字,正则表达式的语法是 \d{1,2}:

url(r'^time/plus/\d{1,2}/$', hours_ahead),

注意 当我们编写Web应用的时候,尽可能考虑可能的数据输入是很重要的,然后决定哪些我们可以接受, 这里,我们就显示了99个小时的时间差。

       我们已经为我们的URL配置好了通配符,我们还需要把它的值传到view function里去,这样我们才能只用一个 view function去处理任意的时间偏移数。我们要做的是在URLpattern里用括号把我们需要的数据括起来。 我们这个例子里,我们需要那个数字作为参数,所以我们把 \d{1,2} 括起来:

url(r'^time/plus/(\d{1,2})/$', hours_ahead),       #注意\d两边的小括号

搞定这些后,让我们来写 hours_ahead 视图了。

hours_ahead 和前面那个 current_datetime 很相似。只有一点不同:它还接受一个参数, 即偏移的小时数 offset 。 代码如下:

from django.http import Http404, HttpResponse
import datetime
def hours_ahead(request, offset):
    try:
        offset = int(offset)
    except ValueError:                         #如果这个值不能被转换成整数,Python会抛出一个 ValueError 异常。
        raise Http404()
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)    #计算时间增减用 datetime.timedelta
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)

精明的读者可能会问:我们在URLpattern的正则表达式中已经用了 (\d{1,2}) 来约束它仅接受 数字了,怎么可能还会有出现 ValueError 的情况呢? 捕获值不是只可能是由数字组成的吗? 答案是: 我们不会这么做,因为URLpattern提供的是“适度但有用”级别的输入校验。万一这个视图 函数被其它方式调用,我们仍需自行检查ValueError。 实践证明,在实现视图函数时,不臆测参数值的做法是比较好的。 松散耦合,还记得么?

    在完成视图函数和URL配置编写后,启动Django开发服务器,用浏览器访问 http://127.0.0.1:8000/time/plus/3/ 来确认它工作正常。 然后是http://127.0.0.1:8000/time/plus/5/ 。再然后是http://127.0.0.1:8000/time/plus/24/ 。

    最后,访问http://127.0.0.1:8000/time/plus/100/ 来检验URL配置里设置的模式是否只 接受一个或两个数字;Django会显示一个 Page not found error 页面, 和以前看到的 404 错误一样。 访问URLhttp://127.0.0.1:8000/time/plus/ (没有 定义时间差) 也会抛 出404错误。

编码顺序 这个例子中,我们先写了URLpattern,然后才写视图,但是上一个例子中我们是先写视图,才是URLpattern。 哪一种方式更好呢?

如果你是一个喜欢从总体上来把握的人,你应该更喜欢在项目开始的时候就写下所有的URL配置。 然后再去编写每个对应的视图。这种方式的一个好处是它会给你一个很清晰的列表,一个to-do list, 还明确地定义了你需要编写的视图函数的参数。

如果你更像一个自底向上的开发者,你可能更喜欢先写视图,然后把他们和URL联系起来。这样也是没问题的。



上一讲:Python网络编程05----django与数据库的交互

下一讲:Python网络编程08----Django模版







欢迎收听我的微信公众号
Python网络编程07----Django中的URLconf_第1张图片


你可能感兴趣的:(django,正则表达式,python,网络编程)