前一章中,咱们解释了如何创建一个 Django 项目并启动 Django 开发服务器。 在这一章,你将会学到用Django建立动态网页的基本知识。html
正如咱们的第一个目标,建立一个网页,用来输出这个著名的示例信息:python
Hello world.程序员
若是你曾经发布过Hello world页面,可是没有使用网页框架,只是简单的在hello.html
文本文件中输入Hello World,而后上传到任意的一个网页服务器上。 注意,在这个过程当中,你已经说明了两个关于这个网页的关键信息: 它包括(字符串 "Hello world"
)和它的URL( http://www.example.com/hello.html
, 若是你把文件放在子目录,也多是 http://www.example.com/files/hello.html
)。web
使用Django,你会用不一样的方法来讲明这两件事 页面的内容是靠view function(视图函数) 来产生,URL定义在 URLconf 中。首先,咱们先写一个Hello World视图函数。正则表达式
在上一章使用django-admin.py startproject
制做的mysite
文件夹中,建立一个叫作views.py
的空文件。这个Python模块将包含这一章的视图。 请留意,Django对于view.py的文件命名没有特别的要求,它不在意这个文件叫什么。可是根据约定,把它命名成view.py是个好主意,这样有利于其余开发者读懂你的代码,正如你很容易的往下读懂本文。数据库
咱们的Hello world视图很是简单。 这些是完整的函数和导入声明,你须要输入到views.py
文件:django
from django.http import HttpResponse def hello(request): return HttpResponse("Hello world")
咱们逐行逐句地分析一遍这段代码:浏览器
首先,咱们从
django.http
模块导入(import)HttpResponse
类。参阅附录 H 了解更多关于HttpRequest
和HttpResponse
的细节。 咱们须要导入这些类,由于咱们会在后面用到。服务器接下来,咱们定义一个叫作
hello
的视图函数。cookie每一个视图函数至少要有一个参数,一般被叫做
request
。 这是一个触发这个视图、包含当前Web请求信息的对象,是类django.http.HttpRequest
的一个实例。在这个示例中,咱们虽然不用request
作任何事情,然而它仍必须是这个视图的第一个参数。注意视图函数的名称并不重要;并不必定非得以某种特定的方式命名才能让 Django 识别它。 在这里咱们把它命名为:hello,是由于这个名称清晰的显示了视图的用意。一样地,你能够用诸 如:hello_wonderful_beautiful_world,这样难看的短句来给它命名。 在下一小节(Your First URLconf),将告诉你Django是如何找到这个函数的。
这个函数只有简单的一行代码: 它仅仅返回一个HttpResponse对象,这个对象包含了文本“Hello world”。
这里主要讲的是: 一个视图就是Python的一个函数。这个函数第一个参数的类型是HttpRequest;它返回一个HttpResponse实例。为了使一个Python的函数成为一个Django可识别的视图,它必须知足这两个条件。 (也有例外,可是咱们稍后才会接触到。
如今,若是你再运行: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。 (这是受配置文件setting中APPEND_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/.
目前,咱们的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是怎么工做的知识. 具体地说,当你经过在浏览器里敲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,(例如,网页内容)。
总结一下:
进来的请求转入/hello/.
Django经过在ROOT_URLCONF配置来决定根URLconf.
Django在URLconf中的全部URL模式中,查找第一个匹配/hello/的条目。
若是找到匹配,将调用相应的视图函数
视图函数返回一个HttpResponse
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文件中更改这个值。请参见它里面 的注释,以得到最新世界时区列表。
如今是好时机来指出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和视图是松耦合的。 咱们将在本书中继续给出这一重要哲学的相关例子。
在咱们的`` 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)
。最终,返回一个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配置里设置的模式是否只 接受一个或两个数字;Django会显示一个 Page not found error 页面, 和之前看到的 404 错误同样。 访问URL http://127.0.0.1:8000/time/plus/
(没有 定义时间差) 也会抛出404错误。
花几分钟时间欣赏一下咱们写好的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 和 POST、cookie 值、元数据(象 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 模式就好了。 (听起来很熟悉? 页面没有发现错误,如前所述,工做正常。)