Python Web框架Django (三)

第三章 视图和URL配置 html

前一章中,咱们解释了如何创建一个 Django 项目并启动 Django 开发服务器。 在这一章,你将会学到用Django建立动态网页的基本知识。 python

你的第一个基于Django的页面: Hello World 程序员

正如咱们的第一个目标,建立一个网页,用来输出这个著名的示例信息: web

Hello world. 正则表达式

若是你曾经发布过Hello world页面,可是没有使用网页框架,只是简单的在hello.html文本文件中输入Hello World,而后上传到任意的一个网页服务器上。 注意,在这个过程当中,你已经说明了两个关于这个网页的关键信息: 它包括(字符串 "Hello world")和它的URL( http://www.example.com/hello.html , 若是你把文件放在子目录,也多是 http://www.example.com/files/hello.html)。 数据库

使用Django,你会用不一样的方法来讲明这两件事 页面的内容是靠view function(视图函数) 来产生,URL定义在 URLconf 中。首先,咱们先写一个Hello World视图函数。 django

第一份视图: 浏览器

在上一章使用django-admin.py startproject制做的mysite文件夹中,建立一个叫作views.py的空文件。这个Python模块将包含这一章的视图。 请留意,Django对于view.py的文件命名没有特别的要求,它不在意这个文件叫什么。可是根据约定,把它命名成view.py是个好主意,这样有利于其余开发者读懂你的代码,正如你很容易的往下读懂本文。 服务器

咱们的Hello world视图很是简单。 这些是完整的函数和导入声明,你须要输入到views.py文件: cookie

from django.http import HttpResponse

 

def hello(request):

return HttpResponse("Hello world")

咱们逐行逐句地分析一遍这段代码:

首先,咱们从 django.http 模块导入(import HttpResponse 类。参阅附录 H 了解更多关于 HttpRequest HttpResponse 的细节。 咱们须要导入这些类,由于咱们会在后面用到。

接下来,咱们定义一个叫作hello 的视图函数。

每一个视图函数至少要有一个参数,一般被叫做request 这是一个触发这个视图、包含当前Web请求信息的对象,是类django.http.HttpRequest的一个实例。在这个示例中,咱们虽然不用request作任何事情,然而它仍必须是这个视图的第一个参数。

注意视图函数的名称并不重要;并不必定非得以某种特定的方式命名才能让 Django 识别它。 在这里咱们把它命名为:hello,是由于这个名称清晰的显示了视图的用意。一样地,你能够用诸如:hello_wonderful_beautiful_world,这样难看的短句来给它命名。 在下一小节(Your First URLconf),将告诉你Django是如何找到这个函数的。

这个函数只有简单的一行代码: 它仅仅返回一个HttpResponse对象,这个对象包含了文本"Hello world"

这里主要讲的是: 一个视图就是Python的一个函数。这个函数第一个参数的类型是HttpRequest;它返回一个HttpResponse实例。为了使一个Python的函数成为一个Django可识别的视图,它必须知足这两个条件。 (也有例外,可是咱们稍后才会接触到。

你的第一个URLconf

如今,若是你再运行:python manage.py runserver,你还将看到Django的欢迎页面,而看不到咱们刚才写的Hello world显示页面。 那是由于咱们的mysite项目还对hello视图一无所知。咱们须要经过一个详细描述的URL来显式的告诉它而且激活这个视图。 (继续咱们刚才相似发布静态HTML文件的例子。如今咱们已经建立了HTML文件,但尚未把它上传至服务器的目录。)为了绑定视图函数和URL,咱们使用URLconf。

URLconf 就像是 Django 所支撑网站的目录。 它的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。 你就是以这种方式告诉 Django,对于这个 URL 调用这段代码,对于那个 URL 调用那段代码。 例如,当用户访问/foo/时,调用视图函数foo_view(),这个视图函数存在于Python模块文件view.py中。

前一章中执行 django-admin.py startproject 时,该脚本会自动为你建了一份 URLconf(即 urls.py 文件)。 默认的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)),

)

默认的URLconf包含了一些被注释起来的Django中经常使用的功能,仅仅只需去掉这些注释就能够开启这些功能. 下面是URLconf中忽略被注释的行后的实际内容

from django.conf.urls.defaults import *

 

urlpatterns = patterns('',

)

让咱们逐行解释一下代码:

  • 第一行导入django.conf.urls.defaults下的全部模块,它们是Django URLconf的基本构造。 这包含了一个patterns函数。
  • 第二行调用 patterns() 函数并将返回结果保存到 urlpatterns 变量。patterns函数当前只有一个参数—一个空的字符串。 (这个字符串能够被用来表示一个视图函数的通用前缀。具体咱们将在第八章里面介绍。)

    当前应该注意是 urlpatterns 变量, Django 指望能从 ROOT_URLCONF 模块中找到它。 该变量定义了 URL 以及用于处理这些 URL 的代码之间的映射关系。 默认状况下,URLconf 全部内容都被注释起来了——Django 应用程序仍是白版一块。 (注:那是上一节中Django怎么知道显示欢迎页面的缘由。 若是 URLconf 为空,Django 会认定你才建立好新项目,所以也就显示那种信息。

    若是想在URLconf中加入URL和view,只需增长映射URL模式和view功能的Python tuple便可. 这里演示如何添加view中hello功能.

    from django.conf.urls.defaults import *

    from mysite.views import hello

     

    urlpatterns = patterns('',

    ('^hello/$', hello),

    )

    请留意:为了简洁,咱们移除了注释代码。 若是你喜欢的话,你能够保留那些行。)

    咱们作了两处修改。

  • 首先,咱们从模块 (在 Python 的 import 语法中, mysite/views.py 转译为 mysite.views ) 中引入了hello 视图。 (这假设mysite/views.py在你的Python搜索路径上。关于搜索路径的解释,请参照下文。)
  • 接下来,咱们为urlpatterns加上一行: ('^hello/$', hello), 这行被称做URLpattern,它是一个Python的元组。元组中第一个元素是模式匹配字符串(正则表达式);第二个元素是那个模式将使用的视图函数。

    简单来讲,咱们只是告诉 Django,全部指向 URL /hello/ 的请求都应由 hello 这个视图函数来处理。

    Python 搜索路径

    Python 搜索路径 就是使用 import 语句时,Python 所查找的系统目录清单。

    举例来讲,假定你将 Python 路径设置为['','/usr/lib/python2.4/site-packages','/home/username/djcode/'] 。若是执行代码from foo import bar ,Python 将会首先在当前目录查找 foo.py 模块( Python 路径第一项的空字符串表示当前目录)。 若是文件不存在,Python将查找 /usr/lib/python2.4/site-packages/foo.py 文件。

    若是你想看Python搜索路径的值,运行Python交互解释器,而后输入:

    >>> import sys

    >>> print sys.path

    一般,你没必要关心 Python 搜索路径的设置。 Python 和 Django 会在后台自动帮你处理好。

    讨论一下URLpattern的语法是值得的,由于它不是显而易见的。 虽然咱们想匹配地址/hello/,可是模式看上去与这有点差异。 这就是为何:

    Django在检查URL模式前,移除每个申请的URL开头的斜杠(/) 这意味着咱们为/hello/URL模式不用包含斜杠(/)。(刚开始,这样可能看起来不直观,但这样的要求简化了许多工做,如URL模式内嵌,咱们将在第八章谈及。)

    模式包含了一个尖号(^)和一个美圆符号($)。这些都是正则表达式符号,而且有特定的含义: 上箭头要求表达式对字符串的头部进行匹配,美圆符号则要求表达式对字符串的尾部进行匹配。

    最好仍是用范例来讲明一下这个概念。 若是咱们用尾部不是$的模式'^hello/',那么任何以/hello/开头的URL将会匹配,例如:/hello/foo /hello/bar,而不只仅是/hello/。相似地,若是咱们忽略了尖号(^),即'hello/$',那么任何以hello/结尾的URL将会匹配,例如:/foo/bar/hello/。若是咱们简单使用hello/,即没有^开头和$结尾,那么任何包含hello/URL将会匹配,如:/foo/hello/bar。所以,咱们使用这两个符号以确保只有/hello/匹配,很少也很多。

    你大多数的URL模式会以^开始、以$结束,可是拥有复杂匹配的灵活性会更好。

    你可能会问:若是有人申请访问/hello(尾部没有斜杠/)会怎样。 由于咱们的URL模式要求尾部有一个斜杠(/),那个申请URL将不匹配。 然而,默认地,任何不匹配或尾部没有斜杠(/)的申请URL,将被重定向至尾部包含斜杠的相同字眼的URL (这是受配置文件settingAPPEND_SLASH项控制的,参见附件D。)

    若是你是喜欢全部URL都以'/'结尾的人(Django开发者的偏心),那么你只须要在每一个URL后添加斜杠,而且设置"APPEND_SLASH""True". 若是不喜欢URL以斜杠结尾或者根据每一个URL来决定,那么须要设置"APPEND_SLASH""False",而且根据你本身的意愿来添加结尾斜杠/URL模式后.

    另外须要注意的是,咱们把hello视图函数做为一个对象传递,而不是调用它。 这是 Python (及其它动态语言的) 的一个重要特性: 函数是一级对象(first-class objects), 也就是说你能够像传递其它变量同样传递它们。 很酷吧?

    启动Django开发服务器来测试修改好的 URLconf, 运行命令行 python manage.py runserver 。 (若是你让它一直运行也能够,开发服务器会自动监测代码改动并自动从新载入,因此不须要手工重启) 开发服务器的地址是http://127.0.0.1:8000/ ,打开你的浏览器访问 http://127.0.0.1:8000/hello/ 。 你就能够看到输出结果了。 开发服务器将自动检测Python代码的更改来作必要的从新加载, 因此你不须要重启Server在代码更改以后。服务器运行地址`` http://127.0.0.1:8000/`` ,因此打开浏览器直接输入`` http://127.0.0.1:8000/hello/`` ,你将看到由你的Django视图输出的Hello world。

    万岁! 你已经建立了第一个Django的web页面。

    正则表达式

    正则表达式 (或 regexes ) 是通用的文本模式匹配的方法。 Django URLconfs 容许你 使用任意的正则表达式来作强有力的URL映射,不过一般你实际上可能只须要使用不多的一 部分功能。 这里是一些基本的语法。

    符号

    匹配

    . (dot)

    任意单一字符

    \d

    任意一位数字

    [A-Z]

    A 到 Z中任意一个字符(大写)

    [a-z]

    a 到 z中任意一个字符(小写)

    [A-Za-z]

    a 到 z中任意一个字符(不区分大小写)

    +

    匹配一个或更多 (例如, \d+ 匹配一个或 多个数字字符)

    [^/]+

    一个或多个不为'/'的字符

    *

    零个或一个以前的表达式(例如:\d? 匹配零个或一个数字)

    *

    匹配0个或更多 (例如, \d* 匹配0个 或更多数字字符)

    {1,3}

    介于一个和三个(包含)以前的表达式(例如,\d{1,3}匹配一个或两个或三个数字)

    有关正则表达式的更多内容,请访问 http://www.djangoproject.com/r/python/re-module/.

    关于"404错误"的快速参考

    目前,咱们的URLconf只定义了一个单独的URL模式: 处理URL /hello/ 。 当请求其余URL会怎么样呢?

    让咱们试试看,运行Django开发服务器并访问相似 http://127.0.0.1:8000/goodbye/ 或者http://127.0.0.1:8000/hello/subdirectory/ ,甚至 http://127.0.0.1:8000/ (网站根目录)。 你将会看到一个 "Page not found" 页面(图 3-2)。 由于你的URL申请在URLconf中没有定义,因此Django显示这条信息。

    图3-1: Django的404 Error页

    这个页面比原始的404错误信息更加实用。 它同时精确的告诉你Django调用哪一个URLconf及其包含的每一个模式。 这样,你应该能了解到为何这个请求会抛出404错误。

    固然,这些敏感的信息应该只呈现给你-开发者。 若是是部署到了因特网上的站点就不该该暴露 这些信息。 出于这个考虑,这个"Page not found"页面只会在 调试模式(debug mode) 下 显示。 咱们将在之后说明怎么关闭调试模式。

    关于网站根目录的快速参考。

    在最后一节,若是你想经过http://127.0.0.1:8000/看网站根目录你将看到一个404错误消息。Django不会增长任何东西在网站根目录,在任何状况下这个URL都不是特殊的 就像在URLconf中的其余条目同样,它也依赖于指定给它的URL模式.

    尽管匹配网站根目录的URL模式不能想象,可是仍是值得提一下的. 当为网站根目录实现一个视图,你须要使用URL模式`` '^$'`` , 它表明一个空字符串。 例如:

    from mysite.views import hello, my_homepage_view

     

    urlpatterns = patterns('',

    ('^$', my_homepage_view),

    # ...

    )

    Django是怎么处理请求的

    在继续咱们的第二个视图功能以前,让咱们暂停一下去了解更多一些有关Django是怎么工做的知识. 具体地说,当你经过在浏览器里敲http://127.0.0.1:8000/hello/来访问Hello world消息得时候,Django在后台有些什么动做呢?

    全部均开始于setting文件。当你运行python manage.py runserver,脚本将在于manage.py同一个目录下查找名为setting.py的文件。这个文件包含了全部有关这个Django项目的配置信息,均大写: TEMPLATE_DIRS , DATABASE_NAME , 等. 最重要的设置时ROOT_URLCONF,它将做为URLconf告诉Django在这个站点中那些Python的模块将被用到

    还记得何时django-admin.py startproject建立文件settings.py和urls.py吗?自动建立的settings.py包含一个ROOT_URLCONF配置用来指向自动产生的urls.py. 打开文件settings.py你将看到以下:

    ROOT_URLCONF = 'mysite.urls'

    相对应的文件是mysite/urls.py

    当访问 URL /hello/ 时,Django 根据 ROOT_URLCONF 的设置装载 URLconf 。 而后按顺序逐个匹配URLconf里的URLpatterns,直到找到一个匹配的。 当找到这个匹配 的URLpatterns就调用相关联的view函数,并把HttpRequest 对象做为第一个参数。 (稍后再给出 HttpRequest 的更多信息) (咱们将在后面看到HttpRequest的标准)

    正如咱们在第一个视图例子里面看到的,一个视图功能必须返回一个HttpResponse。 一旦作完,Django将完成剩余的转换Python的对象到一个合适的带有HTTP头和body的Web Response,(例如,网页内容)。

    总结一下:

  1. 进来的请求转入/hello/.
  2. Django经过在ROOT_URLCONF配置来决定根URLconf.
  3. Django在URLconf中的全部URL模式中,查找第一个匹配/hello/的条目。
  4. 若是找到匹配,将调用相应的视图函数
  5. 视图函数返回一个HttpResponse
  6. Django转换HttpResponse为一个适合的HTTP response, 以Web page显示出来

    你如今知道了怎么作一个 Django-powered 页面了,真的很简单,只须要写视图函数并用 URLconfs把它们和URLs对应起来。 你可能会认为用一系列正则表达式将URLs映射到函数也许会比较慢,但事实却会让你惊讶。

    第二个视图: 动态内容

    咱们的Hello world视图是用来演示基本的Django是如何工做的,可是它不是一个动态网页的例子,由于网页的内容一直是同样的. 每次去查看/hello/,你将会看到相同的内容,它相似一个静态HTML文件。

    咱们的第二个视图,将更多的放些动态的东西例如当前日期和时间显示在网页上 这将很是好,简单的下一步,由于它不引入了数据库或者任何用户的输入,仅仅是输出显示你的服务器的内部时钟. 它仅仅有限度的比Helloworld刺激一些,可是它将演示一些新的概念

    这个视图须要作两件事情: 计算当前日期和时间,并返回包含这些值的HttpResponse 若是你对python颇有经验,那确定知道在python中须要利用datetime模块去计算时间 下面演示如何去使用它:

    >>> import datetime

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

    >>> now

    datetime.datetime(2008, 12, 13, 14, 9, 39, 2731)

    >>> print now

    2008-12-13 14:09:39.002731

    以上代码很简单,并无涉及Django。 它仅仅是Python代码。 须要强调的是,你应该意识到哪些是纯Python代码,哪些是Django特性代码。 (见上) 由于你学习了Django,但愿你能将Django的知识应用在那些不必定须要使用Django的项目上。

    为了让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函数同样,这个函数也保存在view.py中。为了简洁,上面咱们隐藏了hello函数。下面是完整的view.py文件内容:

    from django.http import HttpResponse

    import datetime

     

    def hello(request):

    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:

    在文件顶端,咱们添加了一条语句:import datetime。这样就能够计算日期了。

    函数中的第一行代码计算当前日期和时间,并以 datetime.datetime 对象的形式保存为局部变量 now 

    函数的第二行代码用 Python 的格式化字符串(format-string)功能构造了一段 HTML 响应。 字符串中的%s是占位符,字符串后面的百分号表示用它后面的变量now的值来代替%s。变量%s是一个datetime.datetime对象。它虽然不是一个字符串,可是%s(格式化字符串)会把它转换成字符串,如:2008-12-13 14:09:39.002731。这将致使HTML的输出字符串为:It is now 2008-12-13 14:09:39.002731

    (目前HTML是有错误的,但咱们这样作是为了保持例子的简短。)

    最后,正如咱们刚才写的hello函数同样,视图返回一个HttpResponse对象,它包含生成的响应。

    添加上述代码以后,还要在urls.py中添加URL模式,以告诉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/和新视图。 理解了么?

    写好视图而且更新URLconf以后,运行命令python manage.py runserver以启动服务,在浏览器中输入http://127.0.0.1:8000/time/。 你将看到当前的日期和时间。

    Django时区

    视乎你的机器,显示的日期与时间可能和实际的相差几个小时。 这是由于Django是有时区意识的,而且默认时区为America/Chicago。 (它必须有个值,它的默认值是Django的诞生地:美国/芝加哥)若是你处在别的时区,你须要在settings.py文件中更改这个值。请参见它里面的注释,以得到最新世界时区列表。

    URL配置和松耦合

    如今是好时机来指出Django和URL配置背后的哲学: 松耦合 原则。 简单的说,松耦合是一个 重要的保证互换性的软件开发方法。

    Django的URL配置就是一个很好的例子。 在Django的应用程序中,URL的定义和视图函数之间是松 耦合的,换句话说,决定URL返回哪一个视图函数和实现这个视图函数是在两个不一样的地方。 这使得 开发人员能够修改一块而不会影响另外一块。

    例如,考虑一下current_datetime视图。 若是咱们想把它的URL 从原来的 /time/ 改变到 /currenttime/ ,咱们只须要快速的修改一下URL配置便可, 不用担忧这个函数的内部实现。 一样的,若是咱们想要修改这个函数的内部实现也不用担忧会影响 到对应的URL。

    此外,若是咱们想要输出这个函数到 一些 URL, 咱们只须要修改URL配置而不用 去改动视图的代码。 在这个例子里,current_datetime被两个URL使用。 这是一个故弄玄虚的例子,但这个方法早晚会用得上。

    urlpatterns = patterns('',

    ('^hello/$', hello),

    ('^time/$', current_datetime),

    ('^another-time-page/$', current_datetime),

    )

    URLconf和视图是松耦合的。 咱们将在本书中继续给出这一重要哲学的相关例子。

    第三个视图 动态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),

    )

    很明显,这样处理是不太稳当的。 不但有不少冗余的视图函数,并且整个应用也被限制了只支持 预先定义好的时间段,2小时,3小时,或者4小时。 若是哪天咱们要实现 5 小时,咱们就 不得再也不单首创建新的视图函数和配置URL,既重复又混乱。 咱们须要在这里作一点抽象,提取 一些共同的东西出来。

    关于漂亮URL的一点建议

    若是你有其它web平台的开发经验(如PHP或Java),你可能会想:嘿!让咱们用查询字符串参数吧! 就像/time/plus?hours=3里面的小时应该在查询字符串中被参数hours指定(问号后面的是参数)。

    你 能够 在Django里也这样作 (若是你真的想要这样作,咱们稍后会告诉你怎么作), 可是Django的一个核心理念就是URL必须看起来漂亮。 URL /time/plus/3/ 更加清晰, 更简单,也更有可读性,能够很容易的大声念出来,由于它是纯文本,没有查询字符串那么 复杂。 漂亮的URL就像是高质量的Web应用的一个标志。

    Django的URL配置系统可使你很容易的设置漂亮的URL,而尽可能不要考虑它的 反面 。

    那么,咱们如何设计程序来处理任意数量的时差? 答案是:使用通配符(wildcard URLpatterns)。正如咱们以前提到过,一个URL模式就是一个正则表达式。所以,这里可使用d+来匹配1个以上的数字。

    urlpatterns = patterns('',

    # ...

    (r'^time/plus/\d+/$', hours_ahead),

    # ...

    )

    这里使用# …来表示省略了其它可能存在的URL模式定义。 (见上)

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

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

    备注

    在建造Web应用的时候,尽量多考虑可能的数据输入是很重要的,而后决定哪些咱们能够接受。 在这里咱们就设置了99个小时的时间段限制。

    另一个重点,正则表达式字符串的开头字母"r"。 它告诉Python这是个原始字符串,不须要处理里面的反斜杠(转义字符)。 在普通Python字符串中,反斜杠用于特殊字符的转义。好比n转义成一个换行符。 当你用r把它标示为一个原始字符串后,Python再也不视其中的反斜杠为转义字符。也就是说,"n"是两个字符串:""和"n"。因为反斜杠在Python代码和正则表达式中有冲突,所以建议你在Python定义正则表达式时都使用原始字符串。 从如今开始,本文全部URL模式都用原始字符串。

    如今咱们已经设计了一个带通配符的URL,咱们须要一个方法把它传递到视图函数里去,这样 咱们只用一个视图函数就能够处理全部的时间段了。 咱们使用圆括号把参数在URL模式里标识 出来。 在这个例子中,咱们想要把这些数字做为参数,用圆括号把 \d{1,2} 包围起来:

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

    若是你熟悉正则表达式,那么你应该已经了解,正则表达式也是用圆括号来从文本里 提取 数据的。

    最终的URLconf包含上面两个视图,如:

    from django.conf.urls.defaults import *

    from mysite.views import hello, current_datetime, hours_ahead

     

    urlpatterns = patterns('',

    (r'^hello/$', hello),

    (r'^time/$', current_datetime),

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

    )

    如今开始写 hours_ahead 视图。

    编码次序

    这个例子中,咱们先写了URLpattern ,而后是视图,可是在前面的例子中, 咱们先写了视图,而后是URLpattern 。 哪种方式比较好?

    嗯,怎么说呢,每一个开发者是不同的。

    若是你是喜欢从整体上来把握事物(注: 或译为"大局观")类型的人,你应该会想在项目开始 的时候就写下全部的URL配置。

    若是你从更像是一个自底向上的开发者,你可能更喜欢先写视图, 而后把它们挂接到URL上。 这一样是能够的。

    最后,取决与你喜欢哪一种技术,两种方法都是能够的。 (见上)

    hours_ahead 和咱们之前写的 current_datetime 很象,关键的区别在于: 它多了一个额外参数,时间差。 如下是view代码:

    from django.http import Http404, HttpResponse

    import datetime

     

    def hours_ahead(request, offset):

    try:

    offset = int(offset)

    except ValueError:

    raise Http404()

    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)

    让咱们逐行分析一下代码:

    视图函数hours_ahead ,  两个 参数request  offset . (见上)

    request 是一个 HttpRequest 对象, 就像在 current_datetime 中同样. 再说一次好了: 每个视图 老是以一个 HttpRequest 对象做为 它的第一个参数。 (见上)

    offset 是从匹配的URL里提取出来的。 例如:若是请求URL/time/plus/3/,那么offset将会是3;若是请求URL/time/plus/21/,那么offset将会是21。请注意:捕获值永远都是字符串(string)类型,而不会是整数(integer)类型,即便这个字符串全由数字构成(如:"21")。

    (从技术上来讲,捕获值老是Unicode objects,而不是简单的Python字节串,但目前不须要担忧这些差异。)

    在这里咱们命名变量为 offset ,你也能够任意命名它,只要符合Python 的语法。 变量名是可有可无的,重要的是它的位置,它是这个函数的第二个 参数 ( request 的后面)。 你还可使用关键字来定义它,而不是用 位置。

    咱们在这个函数中要作的第一件事情就是在 offset 上调用 int() . 这会把这个字符串值转换为整数。

    请留意:若是你在一个不能转换成整数类型的值上调用int()Python将抛出一个ValueError异常。如:int('foo')。在这个例子中,若是咱们遇到ValueError异常,咱们将转为抛出django.http.Http404异常——正如你想象的那样:最终显示404页面(提示信息:页面不存在)。

    机灵的读者可能会问: 咱们在URL模式中用正则表达式(d{1,2})约束它,仅接受数字怎么样?这样不管如何,offset都是由数字构成的。 答案是:咱们不会这么作,由于URLpattern提供的是"适度但有用"级别的输入校验。万一这个视图函数被其它方式调用,咱们仍需自行检查ValueError 实践证实,在实现视图函数时,不臆测参数值的作法是比较好的。 松散耦合,还记得么?

    下一行,计算当前日期/时间,而后加上适当的小时数。 current_datetime视图中,咱们已经见过datetime.datetime.now()。这里新的概念是执行日期/时间的算术操做。咱们须要建立一个datetime.timedelta对象和增长一个datetime.datetime对象。 结果保存在变量dt中。

    这一行还说明了,咱们为何在offset上调用int()——datetime.timedelta函数要求hours参数必须为整数类型。

    这行和前面的那行的的一个微小差异就是,它使用带有两个值的Python的格式化字符串功能, 而不只仅是一个值。 所以,在字符串中有两个 %s 符号和一个以进行插入的值的元组: (offset, dt) 

    最终,返回一个HTMLHttpResponse 现在,这种方式已通过时了。

    在完成视图函数和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 错误同样。 访问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):

    # try:

    # offset = int(offset)

    # except ValueError:

    # raise Http404()

    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 to copy-and-paste view"文字。 点击这些字,追溯会 切换另外一个视图,它让你很容易地复制和粘贴这些内容。 当你想同其余人分享这些异常 追溯以得到技术支持时(好比在 Django IRC 聊天室或邮件列表中),可使用它。

    你按一下下面的"Share this traceback on a public Web site"按钮,它将会完成这项工做。 点击它以传回追溯信息至http://www.dpaste.com/,在那里你能够获得一个单独的URL并与其余人分享你的追溯信息。

    接下来的"Request information"部分包含了有关产生错误的 Web 请求的大量信息: GET POSTcookie 值、元数据(象 CGI 头)。 在附录H里给出了request的对象的 完整参考。

    Request信息的下面,"Settings"列出了 Django 使用的具体配置信息。 (咱们已经说起过ROOT_URLCONF,接下来咱们将向你展现各式的Django设置。 附录D覆盖了全部可用的设置。)

    Django 的出错页某些状况下有能力显示更多的信息,好比模板语法错误。 咱们讨论 Django 模板系统时再说它们。 如今,取消 offset = int(offset) 这行的注释,让它从新正常 工做。

    不知道你是否是那种使用当心放置的 print 语句来帮助调试的程序员? 你其实能够用 Django 出错页来作这些,而不用 print 语句。 在你视图的任何位置,临时插入一个 assert False 来触发出错页。 而后,你就能够看到局部变量和程序语句了。 这里有个使用hours_ahead视图的例子:

    def hours_ahead(request, offset):

    try:

    offset = int(offset)

    except ValueError:

    raise Http404()

    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 代码的内部结构以及 Django 配置,在 Internet 上公开这信息是很愚蠢的。 不怀好意的人会尝试使用它攻击你的 Web 应用程序,作些下流之事。 所以,Django 出错信息仅在 debug 模式下才会显现。 咱们稍后 说明如何禁用 debug 模式。 如今,你只要知道 Django 服务器在你开启它时默认运行在 debug 模式就好了。 (听起来很熟悉? 页面没有发现错误,如前所述,工做正常。)

    下一章

    目前为止,咱们已经写好了视图函数和硬编码的HTML。 在演示核心概念时,咱们所做的是为了保持简单。可是在现实世界中,这差很少老是个坏主意。

    幸运的是,Django内建有一个简单有强大的模板处理引擎来让你分离两种工做: 下一章,咱们将学习模板引擎。

相关文章
相关标签/搜索