Django Book 中文翻译版(第二版) 第三章

 第三章 视图和URL配置
上一章中,我们讲解了如何配置一个DJANGO项目,如何运行DJANGO的开发版服务器。本章,你将学习用DJANGO创建动态WEB页面的基本知识。

    第一个DJANGO页面:Hello World
    作为第一个任务,我们现在要创建一个WEB页面,并输出消息“Hello World.“。
    不用WEB框架,你也可以做同样的事情。只需要将”Hello World.”存入一个名为hello.html的文本文件中,然后把它上传到WEB服务器的某个目录中。请注意,在些过程中,你要指定这个WEB页面的两部分信息:显示的内容(“Hello World“)和它对应的URL(http://www.example.com/hello.html,如果你把它放到了某个子目录中,则路径是http://www.example.com/files/hello.html)

        用DJANGO,你要做同样的两件事情,但方式却不同:页面显示的内容是由一个视图方法产生,URL是由URL配置文件指定。先来看如何写一个能输出”Hello World“的视图。

    第一个视图
    在上一章中,曾使用命令django-admin.py startproject在创建了目录mysite,进入此目录,创建一个名为views.py的文件。这个模块将包含本章中的所有视图。关于views.py这个名称,没有任何特殊之处,DJANGO并不关心它叫什么名字,但做为一种惯例,最好叫做views.py,也有有助于其它开发者阅读你的代码。
    这个“Hello World“视图很简单,下面是完整的方法定义,加上import语句,请输入到你的views.py文件中:
from django.http import HttpResponse

def hello(request):
    return HttpResponse("Hello world")

    我们来逐行解释这些代码:
    第一行,我们使用import语句引入类HttpResponse,这个类位于django.http模块中。引入这个类是因为后面代码要使用它。
    第二行,定义了一个名为hello的方法,即视图方法。
    请注意,每个视图方法方法都至少有一个参数,习惯称为request。当视图被触发时,这个参数包含了当前WEB请求的相关信息,它是类django.http.HttpResponse的实例。在本例中,我们没有使用它,但是它仍然要作为这个视图方法的第一个参数。
    注意,不管视图方法名称,不需要以某种命名方式来让DJANGO识别。此处称为hello,完全是因为它指出了方法的主要功能,它也可以被命名为hello_wonderful_beautiful_world,或类似的其它名称。下一节,每一个URL配置中将揭示DJANGO如何找到这个方法。
    方法体只有一行,它只返回了用文本“Hello World”初始化的HttpResponse实例对象。

    本节主要讲解的是:一个视图就是PYTHON中的一个方法,这个方法的第一个参数是HttpRequest类的一个实例,另外这个方法返回结果需要是HttpResponse类的实例。要让PYTHON方法成为DJANGO视图方法,这是必须要做的件事。(不过也有例外,我们稍后讨论)

    第一个URL配置
    到现在为止,如果你运行一次python manage.py runserver,你在浏览器中看到的仍然是“Welcome to Django”,丝毫没有”Hello World”视图的影子。因为我们的项目mysite并不知道有视图hello的存在,我们需要明确的让DJANGO知道,我们要在某个指定的URL上启动这个视图。(正如之前介绍的,与发布静态HTML文件类似,我们现在只是创建了一个HTML文件,还没有将它上传到WEB服务器的某个目录中。)在DJANGO中,为了让视图和URL挂钩,就要使用URL配置了。
    URL配置类似于DJANGO站点中的目录。基本上,它就是一个URL规则与视图方法之间的关系映射,那些URL规则即是要被访问的。那么如何才能让DJANGO知道,对于这个URL,要调用这个视图方法,对于那个URL,要调用那个视图方法呢?例如,当某人访问URL “/foo/“时,调用视图方法foo_view(),这个方法在模块views.py中定义。
    在上一章中,当我们执行命令django-admin.py startproject时,这个命令会自动创建一个URL配置文件,名为urls.py,默认情况下,它看起来像这样:
from django.conf.urls.defaults import *

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('',
    # Example:
    # (r'^mysite/', include('mysite.foo.urls')),

    # Uncomment the admin/doc line below and add 'django.contrib.admindocs'
    # to INSTALLED_APPS to enable admin documentation:
    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    # (r'^admin/', include(admin.site.urls)),
)

    默认的URL配置中包含了一些被注释了的通用的DJANGO设置,以便于在想使用这些设置时只需在适当的行取消注释即可。如果忽略这些注释,它就是以下几行:
from django.conf.urls.defaults import *

urlpatterns = patterns('',
)

    让我们来逐行解释:
第一行从django.conf.urls.defaults模块引入了所有对象,这是URL配置的基础架构,其中包括patterns这个方法调用。
第二行调用方法patterns,把结果保存到变量urlpatterns中。给patterns方法传递了一个参数:空字符串。(这个参数可以用来给方法中的后续参数提供一个公用前缀,我们暂时跳过这种高级用法。)
    这需要注意的是变量urlpatterns,DJANGO会在URL配置模块中查找这个变量。这个变量就是定义了URL和处理对应URL代码的映射关系。正像我们看到的,默认情况下,这个变量为空,也就是说你的应用还只是一个空壳。(补充说明,在上一章中,DJANGO是怎么知道要显示一个“Welcome to Django”的页面?是这样的,如果URL配置中为空,DJANGO就认为你是刚启动了一个新的项目,所以就显示那条信息。)
    要添加一个URL规则和视图方法到URL配置中,只需要添加一个PYTHON的元组,这个元组中有URL规则到视图方法之间的映射关系。下面就是如何将URL规则映射到视图hello:
from django.conf.urls.defaults import *
from mysite.views import hello

urlpatterns = patterns('',
    ('^hello/$', hello),
)
    (注意,为了简短,此处移除了注释的代码,如果愿意,你也可以保留它们。)

    此处我们做了两处修改:
第一是我们从mysite/views.py模块中导入了hello视图,模块mysite/views.py在PYTHON语法中被转化为mysite.views。(假设mysite/views.py在PYTHON可搜索路径中,具体稍后介绍。)
另一个是在变量urlpatterns中添加了一行:('^hello/$',hello),这行就是一个URL规则。它是一个PYTHON元组,第一个元素是规则匹配字符串(可参考正则表达式),第二个元素是规则匹配时所对应的视图方法。
    一句话,我们刚刚是在告诉DJANGO,任何对/hello/的请求都由视图hello来处理。

PYTHON路径
PYTHON路径是你系统上的目录列表,在PYTHON中使用import导入模块时,PYTHON将在这些目录中进行查找。
例如,假设你的PYTHON路径被设置为['','/usr/lib/python2.4/site-packages', '/home/username/djcode']。如果你执行PYTHON语句from foo import bar,PYTHON将在当前目录中查找名为foo.py的模块。(PYTHON路径中的第一个项为串,代指当前目录)如果没找到,PYTHON将查找文件/usr/lib/python2.4/site-packages/foo.py是否存在,如果也不存在,将再试/home/username/djcode/foo.py。最后,如果仍没找到,将引发ImportError错误。
如果你有兴趣查看PYTHON路径中的值,启动PYTHON交互式解释器,输入以下两行:
>>> import sys
>>> print sys.path

一般你不用担心PYTHON路径设置,PYTHON和DJANGO会暗中自动处理。(设置PYTHON路径是脚本manage.py做的事。)

    URL匹配的语法值得一说,因为它可能不太清晰直观。尽管我们想要匹配URL /hello/,但是实际写法还是有点不同。这是因为:
在检查URL匹配规则之前,DJANGO会删除进入URL前面的斜线。这就意味着URL匹配规则中不需要包括开始的斜线。(一开始,这可能不太直观,但是它只简不繁。例如在一个URL配置中可以再包含其它的URL配置,这会在第八章讲解)
匹配规则中包含一个脱字符(^)和一个美元符号($),这两个都是正则表达式中的字符,有特殊含义:脱字符指的是从字符串开始进行匹配,美元符表示匹配字符串的结尾。

    通过例子可以最好的来解释这些概念。如果我们用模式'^hello/'(尾部不带美元符号)来替代'^hello/$',那么任何以/hello/开始的URL都会被匹配,例如/hello/foo和/hello/bar,而不只是/hello/会被匹配。同样,如果我们移除开始的脱字符,改为'hello/$',那么DJANGO会匹配任何以hello/结尾的URL,例如/foo/bar/hello/。如果再修改一下,变为'hello/',没有了开始的脱字符和结尾的美元符,那么任何包含hello/的URL都会被匹配,例如/foo/hello/bar。因此,我们使用脱字符和美元符来确保只有URL /hello/会被匹配。

    许多的URL模式都是以脱字符开始,以美元符结尾,但是要完成更复杂的匹配需要有更好的灵活性。
    你可能会对某人请求URL /hello(不带结尾的斜线)时会发生什么感到疑惑,因为这里的URL模式要求结尾带有斜线,那么这个URL不会被匹配。然而,默认情况下,任何尾部不带斜线的URL请求,若没有匹配的情况下,这个URL都会被在尾部加上斜线而重新请求一次。(在DJANGO中,可以通过APPEND_SLASH设置来进行调整,在附录E中会进行讲解)
    如果你想要所有请求的URL都以斜线结尾(这也是DJANGO开发者所喜欢的方式),那你只需要在每个URL模式中都以斜线结尾,并将APPEND_SLASH设置为True。如果你想要请求的URL不以斜线结尾,或者想要根据每个URL来决定,那就设置APPEND_SLASH为False,并把根据需要把斜线放到URL模式中。

    另外一个需要注意的是,这个URL配置中,我们把视图方法作为一个对象传递,而不是进行方法调用。这是PYTHON(包括其它的动态语言)的一个主要特性:函数是第一类对象,你可以把它们当作变量一样进行传递。酷吧?
    为了测试我们对URL配置的修改,需要启动DJANGO开发服务器。像在第二章中做的一样,运行命令python manage.py runserver来启动。(如果它就一直在运行,那也没问题。开发服务器会自动检测到修改并根据需要重新载入,所以你不用在修改时进行重启。)服务器运行在http://127.0.0.1:8000/,所以打开WEB浏览器,访问http://127.0.0.1:8000/hello/。此时你应该可以看到DJANGO视图的文本输出”Hello World”。
    天啊,终于完成了自己的第一个DJANGO页面!
    
正则表达式
正则表达式(或正则)是用文本表示指定模式的简洁方法。DJANGO的URL配置允许任意的正则以进行强大的URL匹配,实践可能只需要用到部分的正则符号。下面列出了一些常用的符号:
符号
匹配内容
.(点)
任何单个字符
/d
任何单个数字
[A-Z]
在字母A到Z之间的任意字符(大写)
[a-z]
在字母a到z之间的任意字符(小写)
[A-Za-z]
在字母A到Z之间的任意字符(忽略大小写)
+
一个或多个前一表达式的内容
[^/]+
斜线(不包括斜线)之前的多个字符
?
零个或一个前一表达式的内容
*
零个或多个前一表达式的内容
{1,3}
一到三个前一表达式的内容

要了解更多关于正则表达式的内容,请参阅:http://www.djangoproject.com/r/python/re-module/
    
    关于404错误的简要说明
    到此为止,URL配置中只定义了一个URL模式:请求URL/hello/时进行的处理。那若要是请求一个和它不同的URL会发生什么呢?

    想知道结果,试着在WEB浏览器中访问地址:http://127.0.0.1:8000/goodbye/ 或 http://127.0.0.1:8000/hello/subdirectory/或 even http://127.0.0.1:8000/(站点根),是不是看到了”Page not found“的消息页面?DJANGO显示这个消息是因为你的URL配置中没有你访问的URL对应的模式定义。

    此页所显示的内容超出了基本的404错误消息,它也明确的告诉你DJANGO使用的URL配置和URL配置中的每个模式。从这些信息中,你应该知道什么请求的URL会出现404的错误。
    当然,这些敏感信息对作为开发者的你来说是有用的,但是如果你要把站点部署到互联网上的正式环境时,就不能把这信息暴露出来了。因为这个原因,”Page not found“只在DJANGO项目中DEBUG设置True(调试模式)的情况下显示。稍后会讲解如何禁止调试模式。现在只要知道,每个DJANGO项目在第一次被创建之后都是处于调试模式的,如果项目不是调试模式,DJANGO会输出不同的404响应。

    关于站点根的简要说明
    像在上一节的讲解,如果你此时访问站点的根URL:http://127.0.0.1:8000/,同样会看到404错误消息。DJANGO没有对根URL添加任何魔法,它没有任何特殊之处,完全由你决定这个URL模式,就像在URL配置中的其它项一样。

    匹配站点根URL的模式有点不太直观,因此值得一提。当你决定要准备实现站点根URL对应的视图时,请使用URL模式'^$',它匹配空字符串。例如:
urlpatterns = patterns('',
    ('^$', my_homepage_view),
    # ...
)
    DJANGO如何处理请求
    在继续学习下一个视图方法之前,我们先花点时间来了解一个DJANGO的工作情况。准确地说,当你通过WEB浏览器访问http://127.0.0.1:8000/hello/而看到”Hello World”的信息时,DJANGO在后台做了什么呢?
    一切始于设置文件,当运行python manage.py runserver时,命令会在当前目录下查找一个名为settings.py的文件,就像找manage.py。这个文件包括了当前这个DJANGO项目的各种配置,其中的配置项都是大写:如TEMPLATE_DIRS,DATABASE_NAME等。最重要的设置项是ROOT_URLCONF,它会告诉DJANGO哪个PYTHON模块是这个WEB站点的URL配置。
    还记得运行django-admin.py startproject时创建的文件settings.py和urls.py吗?Settings.py文件中包含了一个设置ROOT_URLCONF,其值指向urls.py。现在打开看一下,会有一项是ROOT_URLCONF = 'mysite.urls'。与文件mysite/urls.py相对应。
    
    当一条请求进入时,换句话说,就是对/hello/发起请求时,DJANGO加载由ROOT_URLCONF指向的URL配置。然后顺序检查URL配置中的URL模式,依次将请求的URL与URL模式进行比较,直到找到一个相匹配的。然后调用与那个URL模式所对应的视图方法,并把一个HttpRequest类的对象作为方法的第一个参数。(稍后讲解HttpRequest的特性。)

    正如我们在第一个视图示例中看到的一样,一个视图方法必须返回一个HttpResponse类的实例。一旦方法照这样做了,其余的工作就有DJANGO来完成,它把PYTHON对象转换为一个完整的WEB响应,并带有相应的头和主体(也就是WEB页面的内容。)
    
    总结如下:
1.对/hello/的请求出现时
2.通过检查ROOT_URLCONF设置,确定根URL配置
3.DJANGO检查URL配置中的所有URL模式,找出第一个与/hello/相匹配的
4.若找到匹配的,调用相应的视图方法
5.视图方法返回一个HttpResponse实例方法
6.DJANGO将HttpResponse转换为一个完整的HTTP响应,就是一个WEB页面

    现在你知道了制作一个DJANGO页面的基本概念,说实施,相当简单,只需要写个视图方法,然后通过URL配置把它映射到某个URL即可。

    第二个视图:动态内容
    “Hello World”视图演示了DJANGO工作的基本概念,但是它还不是一个动态页面的例子,因为页面内容总是一样。每次看视图/hello/,都会看到同样的东西,它可以被看作是一个静态HTML文件。

    对于我们将要学习的第二个视图,我们会创建一个更加动态的内容页面,它显示当前日期和时间。好在这一步很简单,因为它不会调用数据库或要求任何的用户输入,只是输出服务器的内部时间。勉强比”Hello World”那个例子更吸引人,但是它将展示几个新的概念。
    
    这个视图要做两件事:计算当前日期和时间值,返回包含这些值的HttpResponse实例。如果你有PYTHON编程经验,你应知道PYTHON包括了一个datetime模块用于处理日期。下面是使用示例:
>>> import datetime

>>> now=datetime.datetime.now()

>>> now

datetime.datetime(2009, 3, 16, 10, 3, 57, 585138)

>>> print now

2009-03-16 10:03:57.585138

    
    这些代码足够简单,与DJANGO毫无关系,只是一些PYTHON代码。(在此我们想要强调,你应该能识别到哪些是PYTHON代码,哪些是DJANGO特定的代码。正如你学习DJANGO一样,我们想要你能够把你的知识应用在没必要使用DJANGO的其它PYTHON项目中。)

    那么,要让DJANGO视图显示当前日期和时间,只需要在视图中调用datetime.datetime.now(),并且返回包含它的HttpResponse实例。下面是相应的代码:
from django.http import HttpResponse
import datetime

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

    像hello视图一样,这段代码应该在views.py中。请注意,在这个例子中我们暂时隐藏了hello方法,但是为了完整性,下面展示了views.py的整个内容:
from django.http import HttpRespon
import datetime

def hello(req):
    return HttpResponse("Hello World!")

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)
    (从现在开始,除非必需,在代码示例中不再显示以前的代码,你应该能从上下文中区分出哪是新的哪是旧的。)

    让我们逐步看看对views.py进行的修改,也就是对应的current_datetime视图。
我们在文件的顶部添加了一条import datetime语句,用于处理日期。
新的current_datetime方法计算出当前日期和时间,作为datetime.datetime实例对象,并把它作为本地变量保存起来。
视图代码中的第二行使用PYTHON的格式化字符串功能构造了一个HTML响应。字符串中的%s是占位符,字符之后的百分符号的意思是,用变量的替换在前一个字符串中的%s。变量now是专门的datetime.datetime对象,不是一个字符串,但是%s格式符将它转换为它的字符串表示,像:2008-12-13 14:09:39。002731。结果就会产生一个HTML字符串例如:<html><body>It is now 2008-12-13 14:09:39.002731.</body></html>。
(当然,这个HTML是无效,我们目的是试图保持示例的简短。)
最后,视图返回一个包含了生成的响应内容的HttpResponse对象,就像我们在hello中做的一样。

    在对views.py进行添加完成之后,要添加URL模式到urls.py中,以便让DJANGO知道哪个URL要调用这个视图。做一个有意义的名称如/time/:
from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime

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

    在这做了两处修改,第一是在顶部引用了current_datetime方法,第二,也是更加重要的是,我们添加了一个URL模式,它将URL/time/映射到新的视图。明白这么做的意义吗?
    完成视图的修改和URL配置的更新后,运行runserver,访问http://127.0.0.1:8000/time/,这时应能看到当前日期与时间。
DJANGO的时区概念
根据你的计算机的不同,日期和时间可能会有几个小时的差距。这是因为DJANGO时区相关的,并且默认设置为America/Chicago时区。(它必须有默认设置,并且这个默认时区与最初的开发者居住地有关。)如果你住在别处,你可以在settings.py中改变它。检查那个文件中的注释,其中有一个链接,指向世界最新时区的列表页面。

    
    URL配置与松散耦合

    现在是个机会来了解一下URL配置和DJANGO背后的主要思想了:松散耦合。简而言之,松散耦合是一种软件开发的方法,它强调可替换代码的重要性。如果两段代码是松散耦合的,那么对其中一段代码的修改将很少或不会影响另一段代码。
    
        DJANGO的URL配置是实践这个原则的好例子。在DJANGO WEB应用中,URL定义和其调用的视图方法之间是松散耦合的,也就是,决定URL和哪个视图方法关联,与视图方法本身的实现,位于两个地方。这就使得切换一段代码不会影响另一段。

    例如,考虑我们的current_datetime视图,如果我们想要改变应用的URL,比如把/time/改为/current-time/,我们可以对URL配置进行快速修改,而不用管视图。同样,如果我们想要修改视图方法,比如替换它的逻辑,或者将它移到另一个PYTHON模块中,我可以照做无妨,不会影响到映射到此视图方法的URL。
    更进一步,如果我们想要当前日期的视图挂到几个URL上,我们可以简单的通过小心修改URL配置达到,而不用接触视力方法的代码。在这个例子中,current_datetime在两个URL上可用。在此有点牵强,但是这种技巧要能够信手拈来:
urlpatterns = patterns('',
    ('^hello/$', hello),
    ('^time/$', current_datetime),
    ('^another-time-page/$', current_datetime),
)

    实践中,URL配置和视图是松散耦合的。贯串本书,我们会继续举出这种重要思想的重要性。

    第三个视图:动态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配置看起来会像下面这样:
urlpatterns = patterns('',
    ('^time/$', current_datetime),
    ('^time/plus/1/$', one_hour_ahead),
    ('^time/plus/2/$', two_hours_ahead),
    ('^time/plus/3/$', three_hours_ahead),
    ('^time/plus/4/$', four_hours_ahead),
)
    很明显,这样的想法有很多缺点。不只是导致过多的视图方法,而且重要的是应用只能支持有限的预先定义的时间范围,1、2、3或4小时。如果我们想要创建一个显示未来5小时的的时间,我们就得创建一个独立的视图和URL配置行,导致重复。因此我们需要在此做些抽象。

    
谈谈优美的URL
1.如果你有其它WEB开发平台的经验,例如PHP或Java,你可能会想,为什么不使用查询字符串参数呢,比如/time/plus?hours=3,偏移的时间将会由URL查询串中的参数hours指定。(?之后的部分)
2.你可以用DJANGO这样做(第七章会详解),但是DJANGO的核心哲学是URL应该是优美的。URL/time/plus/3/会更明晰、简单、更可读,让人更易记住。优美的URL是一个高质量WEB应用的特点之一。
3.通过让使用优美的URL比不使用更加简单,DJANGO的URL配置系统鼓励使用优美的URL。

    那么,我们该如何设计应用的URL让它能处理任意的时间偏移呢?答案是使用URL模式通配符。之前我们提到,一个URL模式其实是一个正则表达式,因此,可以使用正则表达式模式/d+来匹配1或多个数字:
#...
(r'^time/plus//d+/$', hours_ahead),
#...

    (使用#...表示可能还有其它的url模式,不过在此例中被去除了。)
    
    新的URL模式会匹配符合的任意URL,例如/time/plus/2/,/time/plus/25,甚至是/time/plus/1000000000。想起来了,此处需要数字长度以便让偏移最大只到99。这就意味着只允许出现1或2位数字,用正则表达式表示,就是/d{1,2};
(r'^time/plus//d{1,2}/$',hours_ahead),

注意
在构建WEB应用时,考虑到可能的异常数据输入,及决定应用是否要支持这种输入总是很重要的。此处我们通过限制偏移到最大99来杜绝异常输入的可能。

    此处讲解的过程中一个更重要的细节是正则表达式字符串前面的r字符,它告诉PYTHON这是一个raw string,也就是内容中的斜线没有特殊含义。一个普通的PYTHON字符串中,斜线是用来转义的特殊字符,例如'/n'表示包含新行的单个字符的字符串。如果在它前面加上r让它成为raw string,那么PYTHON就不再进行斜线转义,所以,r'/n'就是一个包含了两个字符的字符串,一个是斜线,一个是小写字母'n'。PYTHON中的斜线用法和正则表达式中出现的斜线之间有一点冲突,因此强烈建议在PYTHON中定义正则表达式时使用raw string。从现在起,本书中的URL模式都将是raw string。

    现在我们已经指定了针对URL的通配符,但是需要某种方法将匹配的数据传递给视图方法,以便能使用一个视图方法处理任意的时间偏移。要达到这个上的,只需要将URL模式中需要提取的数据外面放上圆括号即可。此例中,我们要提取的是数字,因此在/d{1,2}外面加上括号:
(r'^time/plus/(/d{1,2})/$', hours_ahead),
    
    如果你熟悉正则表达式,此处应该信手拈来,我们使用圆括号从匹配文本中获取数据。

    最终的URL配置,包括了前面的两个视图,如下:
from django.conf.urls.defaults import *
from views import *


urlpatterns = patterns('',
    (r'^hello/$', hello),
    (r'^time/$', current_datetime),
    (r'^time/plus/(/d{1,2})/$', hours_ahead),
)

    接下来实现hours_ahead视图。
编码顺序
在此例中,我们先写好URL模式,然后实现视图方法,但是在前一个例子中,我们是先写的视图,然后才写的URL模式。到底哪更好呢?
其实每个开发者都不同。
如果你是一个有大局观的人,你可能会在项目一开始就写好应用中的所有URL模式,然后再进行视图编码。它的优点是你会因此有一个清晰的任务列表,也基本上定义了视图方法所需的参数。
如果你是一个自底向上的开发者,你可能更愿意写视图,然后再把它们和URL对应起来。这也可以。
最后,结论就是,哪种方法适合你,它就是最好的。两种方法都有效。

    hours_ahead与前面写的current_datetime视图非常相似,主要区别是:hours_ahead有一个额外的参数,表示小时偏移数。代码如下:
def hours_ahead(request, offset):
    offset = int(offset)
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)
    我们来逐行解释:
    
它有两个参数:request和offset.
Request是HttpResponse实例对象,就像hello和current_datetime中的一样。再重复一遍,每个视图总会有个HttpResponse实例对象作为每一个参数。
offset是与URL模式中圆括号内匹配的字符串值。例如,如果URL是/time/plus/3/,那么offset的值就是字符串'3'。如果请求是/time/plus/21/,那么offset就是字符串'21'。注意匹配的值总是字符串,不是整数,即使这个字符串是由数字字符构成的如'21'。
                (从技术角度来说,捕获的值总是Unicode对象,而不是PYTHON字节型字符串,此时暂可                不用考虑)
                我们可以调用变量offset,并且任何时候都可以像调用有效的PYTHON标识符一样调用它。变 量名不重要,重要的是对于方法说,它是request之后的第二个参数。(也有可能在URL配置 中使用关键字参数而不是位置。第八章会讲解。)

方法做的第一件事是在offset上调用int(),它将字符串转换为整数。
    请注意,如果对一个不能被转换为整数的值调用int()将会引发ValueError异常,例如字符串'foo'。然而在本例中我们不用担心会出现这个异常,因为我们可以确保offset是一个包含数字的字符串。并且原因就是在URL模式中的正则表达式(/d{1,2})只匹配数字。这同时表现了URL配置的另一优点:提供了一种适当并且有用的输入验证标准。
方法的下一行,计算当前日期/时间,然后加一个小时数。
    在current_datetime视图中已经见过datetime.datetime.now()了,此处的新知识是通过创建一个datetime.timedelta对象,并把它和一个datetime.datetime对象相加可以进行日期/时间运算。此处的结果存在变量dt中。
    这一行也表明了为什么我们要在offset上调用int(),因为datetime.timedelta方法的hours参数要求是一个整数。
下一行,方法构造出HTML输出,就像在current_datetime中一样,但是一个小区别是此行使用PYTHON的格式化字符串功能,并且带两个参数而不是一个,因此在字符串中有两个%s符号,一个元组的数据被相应的插入:(offset, dt)。
最后,返回带HTML内容的HttpResponse对象,到现在为止,这是老调重弹。

    完成视图方法和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配置中的模式是否是只接受1或2位数字,在目前的情形,DJANGO应该显示一个”Page not found”的错误,就像在之前“404错误的快速说明”一节中的一样。URL http://127.0.0.1:8000/time/plus/(不指定小时)也是引发404错误。

    DJANGO人性化的错误页

    花点时间来赞美一下我们做到现在的WEB应用,现在先来破坏一下。在views.py文件中故意弄一个PYTHON错误,注释hours_ahead方法的offset=int(offset)一行:
def hours_ahead(request, offset):
##    offset = int(offset)
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)

    访问/time/plus/3/,你会看到一堆信息的错误页,包括一个显示在页面顶端的TypeError信息:
unsupported type for timedelta hours component: unicode

    发生了什么呢?datetime.timedelta方法要求hours参数是一个整数,而我们注释了把offset转换为整数的代码,这就导致了datetime.timedelta会引发TypeError错误。这是每个程序员都会犯的典型的错误。

    本例的上的是揭示DJANGO的错误页,所以花点时间来浏览一下这个页面,了解一下提供的不同信息。
    下面是需要注意的地方:
页面顶部,看到的是异常的主要信息:异常的类型,传给异常的参数(此处是”unsupported type“消息),异常被引发于哪个文件以及行号。
在主要异常信息下面,页面显示了完整的PYTHON调用堆栈,这有点像标准的PYTHON命令行解释器调用堆栈,不同的是后者更多的是交互。对于在堆栈中的每一级(“帧”),DJANGO都显示了文件名称,方法名,行号和那一行的源代码。
        
点击源代码行(灰色部分),你会看到给你提供上下文的错误行的前后几行。
点击任意帧下的“Local vars”,会看到有一张局部变量和值的表,它表明异常引发时代码的确切位置。这些调试信息很有帮助。
注意在“Traceback“旁边的”Switch back to interactive view“,点击这个链接,调用堆栈会切换到另一样式,此样式可以很容易的复制和粘贴。当你想要给另一人提供你的异常调用堆栈信息以获取相关支持时可以使用这个功能,例如DJANGO IRC聊天室或DJANGO用户邮件列表中的人们。
再往下,有一个”Share this traceback on a public Web site“按钮让你只要点击一下就可完成这工作。点击它把调用堆栈信息提交到http://www.dpaste.com,然后可以获取一个你能分享信息的人们的URL。
接下来,是“Request information”,它包含了造成错误的WEB请求的有用信息:GET和POST信息,cookie值,meta信息例如CGI头。
附录H有一个关于请求对象包括的所有信息的完整参考。
在“Request information”下面,“Settings“部分列出了当前DJANGO应用的特定设置。(之前已经提过ROOT_URLCONF,贯串本书,我们会讲解许多不同的DJANGO设置。完整的设置细节在附录E中有论述。)

    DJANGO错误页在某些特殊情况下会显示更多的信息,例如模板语法错误时。在讨论DJANGO模板系统时会进行讲解,现在,去掉offset=int(offset)行的注释,让视图方法又能正常工作。

    你是那种小心放一些print语句来帮助进行调试的程序员吗?你可以使用DJANGO错误页做同样的事,只是不用print语句了。在视图的任意位置,临时性的插入一个assert False可引发错误页面,然后就可查看本地变量和程序状态,下面是例子,使用hours_ahead视图:
def hours_ahead(request, offset):
    offset = int(offset)
    dt = datetime.datetime.now() + datetime.timedelta(hours=offset)
    assert False
    html = "<html><body>In %s hour(s), it will be %s.</body></html>" % (offset, dt)
    return HttpResponse(html)

    最后,很明显这些信息都是很敏感的,它暴露了你的PYTHON代码的内部结构和DJNAGO配置,所以在正式环境展示是不明智的。一个不怀好意的人有可能会使用这些信息对你的WEB应用进行反向工程,然后做一些不正当的事情,所以DJANGO错误页只在你DJANGO项目处于调试模式时显示。在第二十章讲解如果禁用调试模式。现在,你要知道,每个DJANGO项目在一开始都是自动处于调试模式的。(有点耳熟?本章前面描述的“Page not found”错误与此类似,一样的工作方式)

    接下来?

    到此为止,我们写的视图方法都是直接在PYTHON代码中硬编码HTML,我们这样做是为了保持展示核心概念的简单,但是在现实应用中,这完全是错误的。

    DJANGO附带了一个简单但是功能强大的模板引擎,它让页面设计从代码中分离,下一章我们将开始DJANGO模板引擎的讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值