第三章:动态网页基础

优质
小牛编辑
126浏览
2023-12-01

前一章中,我们解释了如何建立一个 Django 项目并启动 Django 开发服务器。当然,那个网站实际并没有干什么有用的事情,它所做的只是显示 It worked! 消息。让我们来做些改变。本章将介绍如何使用 Django 创建动态网页。

第一份视图:动态内容

我们的第一个目标是创建一个显示当前日期和时间的网页。这是一个不错的 动态 网页范例,因为该页面的内容不是静态的。相反,其内容是随着计算(本例中是对当前时间的计算)的结果而变化的。这个简单的范例既不涉及数据库,也不需要任何用户输入,仅输出服务器的内部时钟。

我们将编写一个 视图函数 以创建该页面。所谓的视图函数(或 视图 ),只不过是一个接受 Web 请求并返回 Web 响应的 Python 函数。实际上,该响应可以是一份网页的 HTML 内容、一次重定向、一条 404 错误、一份 XML 文档、一幅图片,或其它任何东西。视图本身包含返回该响应所需的任意逻辑。该段代码可以随意放置,只要在 Python 的路径设置中就可以了。没有其它要求——也可以说是没有任何奇特之处。为了给这些代码一个 存身之处 ,让我们在上一章所创建的 mysite 目录中新建一份名为 views.py 的文件。

以下是一个以 HTML 方式返回当前的日期与时间的视图 (view),: 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)

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

首先,我们从 django.http 模块导入(import) HttpResponse 类。参阅附录 H 了解更多关于 HttpRequest 和 HttpResponse 的细节。

然后我们从 Python 标准库(Python 自带的实用模块集合)中导入(import) datetime 模块。

datetime 模块包含几个处理日期和时间的函数(functions)和类(classes),其中就包括返回当前时间的函数。

接下来,我们定义了一个叫做 current_datetime 的函数。这就是所谓的视图函数(view function)。每个视图函数都以一个 HttpRequest 对象为第一个参数,该参数通常命名为

request 。

注意视图函数的名称并不重要;并不一定非得以某种特定的方式命名才能让 Django 识别它。此处,我们称之为 current_datetime ,只是因为该名字明确地指出了它的功能,而它也可以被命名为 super_duper_awesome_current_time 或者其它同样莫名其妙的名字。Django 并不关心其名字。下一节将解释 Django 如何查找该函数。

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

函数的第二行代码用 Python 的格式化字符串(format-string)功能构造了一段 HTML 响应。字符串里面的 %s 是占位符,字符串之后的百分号表示使用变量 now 的值替换 %s 。(是的,这段 HTML 不合法,但我们只不过是想让范例尽量保持简短而已。)

最后,该视图返回一个包含所生成响应的 HttpResponse 对象。每个视图函数都负责返回一个 HttpResponse 对象。(也有例外,但是我们稍后才会接触到。)

Django 时区 (Time Zone)

Django 包含一个默认为 America/Chicago 的 TIME_ZONE 设置。这可能不是你所居住的时区,因此你可以在 settings.py 文件中修改它。请参阅附录 E 了解更多细节。

将 URL 映射到视图

那么概括起来,该视图函数返回了包含当前日期和时间的一段 HTML 页面。但是如何告诉

Django 使用这段代码呢?这就是 URLconfs 粉墨登场的地方了。

URLconf 就像是 Django 所支撑网站的目录。它的本质是 URL 模式以及要为该 URL 模式调用的视图函数之间的映射表。你就是以这种方式告诉 Django,对于这个 URL 调用这段代码,对于那个 URL 调用那段代码。但必须记住的是视图函数必须位于 Python 搜索路径之中。

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 文件。如果文件也不存在,它将尝试

/home/username/djcode/foo.py 。最后,如果 这个 文件还不存在,它将引发 ImportError异常。

如果对了解 Python 搜索路径值感兴趣,可以启动 Python 交互式解释程序,输入 import sys ,接着输入 print sys.path 。

通常,你不必关心 Python 搜索路径的设置。Python 和 Django 会在后台自动帮你处理好。

(如果有兴趣了解的话,Python 搜索路径的设置工作是 manage.py 文件的职能之一。)

前一章中执行 django-admin.py startproject 时,该脚本会自动为你建了一份 URLconf(即

urls.py 文件)。让我们编辑一下这份文件。缺省情况下它是下面这个样子: from django.conf.urls.defaults import *

urlpatterns = patterns('',

# Example:

# (r'^mysite/', include('mysite.apps.foo.urls.foo')),

# Uncomment this for admin:

# (r'^admin/', include('django.contrib.admin.urls')),

)

让我们逐行逐句分析一遍这段代码:

  • 第一行从 django.conf.urls.defaults 模块引入了所有的对象,其中包括了叫做

    patterns 的函数。

  • 第二行调用 patterns() 函数并将返回结果保存到 urlpatterns 变量。 patterns()函数只传入了一个空字符串参数。其他代码行都被注释掉了。 (该字符串可用作视图函数的通用前缀,但目前我们将略过这种高级用法。)

当前应该注意是 urlpatterns 变量, Django 期望能从 ROOT_URLCONF 模块中找到它。该变量定义了 URL 以及用于处理这些 URL 的代码之间的映射关系。

默认情况下,URLconf 所有内容都被注释起来了——Django 应用程序还是白版一块。(旁注:这也就是上一章中 Django 显示“It worked!”页面的原因。如果 URLconf 为空,Django 会认定你才创建好新项目,因此也就显示那种信息。)

现在编辑该文件以展示我们的 current_datetime 视图: from django.conf.urls.defaults import *

from mysite.views import current_datetime

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

)

我们做了两处修改。首先,我们从模块 (在 Python 的 import 语法中, mysite/views.py 转译为 mysite.views ) 中引入了 current_datetime 视图。接着,我们加入了 (r'^time/$',

current_datetime), 这一行。该行就是所谓的 URLpattern ,它是一个 Python 元组,其第

一个元素是简单的正则表达式,第二个元素是为该模式应用的视图函数。

简单来说,我们只是告诉 Django,所有指向 URL /time/ 的请求都应由 current_datetime这个视图函数来处理。

下面是一些需要注意的地方:

注意,该例中,我们将 current_datetime 视图函数作为对象传递,而不是调用它。这是 Python (及其它动态语言的) 的一个重要特性:函数是一级对象(first-class objects),也就是说你可以像传递其它变量一样传递它们。很酷吧?

r'^time/$' 中的 r 表示 '^time/$' 是一个原始字符串。这样一来就可以避免 正则表达式有过多的转义字符。

不必在 '^time/$' 前加斜杠(/)来匹配 /time/ , 因为 Django 会自动在每个表 达式前添加一个斜杠。乍看起来,这好像有点奇怪,但是 URLconfs 可能由其它的 URLconfs 所引用, 所以不加前面的斜杠可让事情简单一些。这一点在第 8 章中将有进一步阐述。

上箭头 ^ 和美元符号 $ 符号非常重要。上箭头要求表达式对字符串的头部进行匹配,美元符号则要求表达式对字符串的尾部进行匹配。

最好还是用范例来说明一下这个概念。如果我们用 '^time/' (结尾没有$), 那么以 time/开始的 任意 URL 都会匹配,比如 /time/foo 和 /time/bar ,不仅仅是 /time/ 。同样的,如果我们去掉最前面的 ^ ( 'time/$' ), Django 一样会匹配由 time/ 结束的 任意 URL

/time/ ,比如 /foo/bar/time/ 。 因此,我们必须同时用上 ^ 和 $ 来精确匹配 URL

/time/ 。不能多也不能少。

你可能想如果有人请求 /time 也可以同样处理。如果 APPEND_SLASH 的 设置是 True 的话,系统会重定向到 /time/ ,这样就可以一样处理了。 (有关内容请查看附录 E )

启动 Django 开发服务器来测试修改好的 URLconf, 运行命令行 python manage.py

runserver 。 (如果你让它一直运行也可以,开发服务器会自动监测代码改动并自动重新载入,所以不需要手工重启) 开发服务器的地址是 http://127.0.0.1:8000/ ,打开你的浏览器访问 http://127.0.0.1:8000/time/ 。 你就可以看到输出结果了。

万岁!你已经创建了第一个 Django 的 web 页面。正则表达式

正则表达式 (或 regexes ) 是通用的文本模式匹配的方法。Django URLconfs 允许你 使用

任意的正则表达式来做强有力的 URL 映射,不过通常你实际上可能只需要使用很少的一 部分功能。下面就是一些常用通用模式:

符号

匹配

. (dot)

任意字符

\d

任意数字

符号

匹配

[A-Z]

任意字符, A-Z (大写)

[a-z]

任意字符, a-z (小写)

[A-Za-z]

任意字符, a-z (不区分大小写)

+

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

[^/]+

不是/的任意字符

*

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

{1,3}

匹配 1 个到 3 个(包含)3

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

Django 是怎么处理请求的

我们必须对刚才所发生的几件事情进行一些说明。它们是运行 Django 开发服务器和构造 Web页面请求的本质所在。

命令 python manage.py runserver 从同一目录载入文件 settings.py 。 该文件包含了这个特定的 Django 实例所有的各种可选配置,其中一个最重要的配置就是 ROOT_URLCONF 。

ROOT_URLCONF 告诉 Django 哪个 Python 模块应该用作本网站的 URLconf。

还记得 django-admin.py startproject 创建的文件 settings.py 和 urls.py 吗? 这时系统自动生成的 settings.py 里 ROOT_URLCONF 默认设置是 urls.py 。

当访问 URL /time/ 时,Django 根据 ROOT_URLCONF 的设置装载 URLconf 。 然后按顺序逐个匹配 URLconf 里的 URLpatterns,直到找到一个匹配的。当找到这个匹配 的 URLpatterns就调用相关联的 view 函数,并把 HttpRequest 对象作为第一个参数。 (稍后再给出

HttpRequest 的更多信息)

该 view 函数负责返回一个 HttpResponse 对象。

你现在知道了怎么做一个 Django-powered 页面了,真的很简单,只需要写视图函数并用

URLconfs 把它们和 URLs 对应起来。你可能会认为用一系列正则表达式将 URLs 映射到函数也许会比较慢,但事实却会让你惊讶。

Django 如何处理请求: 完整细节

除了刚才所说到的简明 URL-to-view 映射方式之外,Django 在请求处理方面提供了大量的灵活性。

通过 URLconf 解析到哪个视图函数来返回 HttpResponse 可以通过中间件(middleware) 来短路或者增强。关于中间件的细节将在第十五章详细谈论,这里给出 图 3-1 让你先了解 大体概念.

image

图 3-1:Django 请求回应流程

当服务器收到一个 HTTP 请求以后,一个服务器特定的 handler 会创建 HttpRequest 并传递给下一个组件并处理。

这个 handler 然后调用所有可用的 Request 或者 View 中间件。这些类型的中间件通常是用来 增强 HttpRequest 对象来对一些特别类型的 request 做些特别处理。只要其中有一个 返回 HttpResponse ,系统就跳过对视图的处理。

即便是最棒的程序员也会有出错的时候, 这个时候 异常处理中间件(exception middleware) 可以帮你的大忙。如果一个视图函数抛出异常,控制器会传递给异常处理中间件处理。如果这个 中间件没有返回 HttpResponse ,意味着它不能处理这个异常,这个异常将会再次抛出。

即便是这样,你也不用担心。Django 包含缺省的视图来生成友好的 404 和 500 回应

(response)。

最后, response middleware 做发送 HttpResponse 给浏览器之前的后处理或者清除 请求用到的相关资源。

URL 配置和松耦合

现在是好时机来指出 Django 和 URL 配置背后的哲学: 松耦合 原则。简单的说,松耦合是一个 重要的保证互换性的软件开发方法。如果两段代码是松耦合的,那么改动其中一段代码不会 影响另一段代码,或者只有很少的一点影响。

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

相比之下,其他的 Web 开发平台紧耦合和 URL 到代码中。在典型的 PHP (http://www.php.net/)应用,URL 的设计是通过放置代码的目录来实现。在早期的 CherryPy Python Web framework

(http://www.cherrypy.org/) 中,URL 对应处理的的方法名。这可能在短期看起来是便利之举, 但是长期会带来难维护的问题。

比方说,考虑有一个以前写的视图函数,这个函数显示当前日期和时间。如果我们想把它的

URL 从原来的 /time/ 改变到 /currenttime/ ,我们只需要快速的修改一下 URL 配置即可,不用担心这个函数的内部实现。同样的,如果我们想要修改这个函数的内部实现也不用担心会影响 到对应的 URL。此外,如果我们想要输出这个函数到 一些 URL, 我们只需要修改 URL配置而不用 去改动视图的代码。

Django 大量应用松耦合。我们将在本书中继续给出这一重要哲学的相关例子。

404 错误

在我们当前的这个 URL 配置中,我们之定义了一个 URL 模式:处理 URL /time/ 。 当请求其他 URL 会怎么样呢?

让我们试试看,运行 Django 开发服务器并访问类似 http://127.0.0.1:8000/hello/ 或者 http://127.0.0.1:8000/does-not-exist/ ,甚至 http://127.0.0.1:8000/ (网站根目录)。你将会看到一个 “Page not found” 页面(图 3-2)。(挺漂亮的,是吧? 你会喜欢上我们的配色方案的;-) 如果请求的 URL 没有在 URL 配置里设置,Django 就会显示这个页面。

image

图 3-2. Django 的 404 页面

这个页面的功能不只是显示 404 的基本错误信息,它同样精确的告诉你 Django 使用了哪个

URL 配置和 这个配置里的每一个模式。这样,你应该能了解到为什么这个请求会抛出 404 错误。

当然,这些敏感的信息应该只呈现给你-开发者。如果是部署到了因特网上的站点就不应该暴露 这些信息。出于这个考虑,这个“Page not found”页面只会在 调试模式(debug mode)下 显示。我们将在以后说明怎么关闭调试模式。现在,你只需要知道所有的 Django 项目在创建后都 是在调试模式下的,如果关闭了调试模式,结果将会不一样。

第二个视图:动态 URL

在我们的第一个视图范例中,尽管内容是动态的,但是 URL ( /time/ )是静态的。在 大多数动态 web 应用程序,URL 通常都包含有相关的参数。

让我们创建第二个视图来显示当前时间和加上时间偏差量的时间,设计是这样的:

/time/plus/1/ 显示当前时间+1 个小时的页面 /time/plus/2/ 显示当前时间+2 个小时的页面 /time/plus/3/ 显示当前时间+3 个小时的页面,以此类推。

新手可能会考虑写不同的视图函数来处理每个时间偏差量,URL 配置看起来就象这样:

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

(r'^time/plus/1/$', one_hour_ahead), (r'^time/plus/2/$', two_hours_ahead), (r'^time/plus/3/$', three_hours_ahead), (r'^time/plus/4//$', four_hours_ahead),

)

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

关于漂亮 URL 的一点建议

如果你有其他 Web 开发平台的经验,例如 PHP 或者 JAVA,你可能会想,好吧,让我们来用一个 查询字符串参数来表示它们吧,例如 /time/plus?hours=3 ,哪个时间段用 hours 参数代表,URL 的查询字符串(query string)是 URL 里 ? 后面的字符串。

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

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

带通配符的 URL 匹配模式

继续我们的 hours_ahead 范例,让我们在 URL 模式里使用通配符。我们前面讲到,URL 模式是一个正则表达式,因此,我们可以使用正则表达式模式 \d+ 来匹配一个或多个数字:

from django.conf.urls.defaults import *

from mysite.views import current_datetime, hours_ahead

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

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

)

这个 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 个小时的时间段限制。

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

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

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

最终的 current_datetime URLconf,包含我们前面的视图,看起来像这样: from django.conf.urls.defaults import *

from mysite.views import current_datetime, hours_ahead

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

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

)

现在开始写 hours_ahead 视图。编码次序

这个例子中,我们先写了 URLpattern ,然后是视图,但是在前面的例子中, 我们先写了视图,然后是 URLpattern 。哪种技术更好?嗯,怎么说呢,每个开发者是不一样的。

如果你是喜欢从总体上来把握事物(注:或译为“大局观”)类型的人,你应该会想在项目开始 的时候就写下所有的 URL 配置。这会给你带来一些好处,例如,给你一个清晰的 to-do列表,让你 更好的定义视图所需的参数。

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

最后,取决与你喜欢哪种技术,两种方法都是可以的。

hours_ahead 和我们以前写的 current_datetime 很象,关键的区别在于: 它多了一个额外参数,时间差。 views.py 修改如下:

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)

让我们一次一行通一下这些代码:

就像我们在我们的 current_datetime 视图中所作的一样,我们导入

django.http.HttpResponse 类和 datetime 模块。

视图函数, hours_ahead , 有 两个 参数: request 和 offset .

request 是一个 HttpRequest 对象, 就像在 current_datetime 中一样. 再说一次好了:每一个视图 总是 以一个 HttpRequest 对象作为 它的第一个参数。

offset 是从匹配的 URL 里提取出来的。例如: 如果 URL 是 /time/plus/3/ 那么 offset 是字符串 '3' , 如果 URL 是 /time/plus/21/ ,那么 offset 是字符串 '21' , 注意,提取的字符串总是 字符串 ,不是整数,即便都是数字组成,就象 '21' 。

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

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

注意 Python 可能会在你调用 int() 来转换一个不能转换成整数时抛出 ValueError 异常,例如字符串 'foo' 。当然,在这个范例中我们不用担心这个问题,因为我们已经确定 offset是 只包含数字字符的字符串。因为正则表达式 (\d{1,2}) 只提取数字字符。 这也是 URL 配置的另一个好处:提供了清晰的输入数据有效性确认。

下一行显示了我们为什么调用 int() 来转换 offset 。 这一行我们要 计算当前时间加上这个时间差 offset 小时,保存结果到变量 dt 。 datetime.timedelta 函数的参数 hours 必须是整数类型。

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

最后,我们再一次返回一个 HTML 的 HttpResponse ,就像我们在 current_datetime 做的一样。

在完成视图函数和 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 错误。

你现在已经注意到 views.py 文件中包含了两个视图, views.py 看起来象这样: 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)

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)

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: str" 。

怎么回事呢?是的, datetime.timedelta 函数要求 hours 参数必须为整型, 而我们注释掉了将 offset 转为整型的代码。这样导致 datetime.timedelta 弹出 TypeError 异常。这是所有程序员某个时候都可能碰到的一种典型错误。

这个例子是为了展示 Django 的出错页面。我们来花些时间看一看这个出错页,了解一下其中 给出了哪些信息。

以下是值得注意的一些要点:

在页面顶部,你可以得到关键的异常信息:异常数据类型、异常的参数 (如本例中的 "unsupported type" )、在哪个文件中引发了异常、出错的行号等等。

在关键异常信息下方,该页面显示了对该异常的完整 Python 追踪信息。这类似于你在

Python 命令行解释器中获得的追溯信息,只不过后者更具交互性。对栈中的每一帧,Django均显示了其文件名、函数或方法名、行号及该行源代码。

点击该行代码 (以深灰色显示),你可以看到出错行的前后几行,从而得知相关上下文情况。

点击栈中的任何一帧的“Local vars”可以看到一个所有局部变量的列表,以及在出错 那一帧时它们的值。这些调试信息是无价的。

注意“Traceback”下面的“Switch to copy-and-paste view”文字。点击这些字,追溯会 切换另一个视图,它让你很容易地复制和粘贴这些内容。当你想同其他人分享这些异常 追溯以获得技术支持时(比如在 Django 的 IRC 聊天室或邮件列表中),可以使用它。

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

Request 信息的下面,“Settings”列出了 Django 使用的具体配置信息。同样在附录 E 给出了 settings 配置的完整参考。现在,大概浏览一下,对它们有个大致印象就好了。

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

不知道你是不是那种使用小心放置的 print 语句来帮助调试的程序员?你其实可以用

Django 出错页来做这些,而不用 print 语句。在你视图的任何位置,临时插入一个 assert

False 来触发出错页。然后,你就可以看到局部变量和程序语句了。(还有更高级的办法来调试 Django 视图,我们后来再说,但这个是最快捷最简单的办法了。)

最后,很显然这些信息很多是敏感的,它暴露了你 Python 代码的内部结构以及 Django 配置,在 Internet 上公开这信息是很愚蠢的。不怀好意的人会尝试使用它攻击你的 Web 应用程序,做些下流之事。因此,Django 出错信息仅在 debug 模式下才会显现。我们稍后 说明如何禁用 debug 模式。现在,你只要知道 Django 服务器在你开启它时默认运行在 debug 模式就行了。(听起来很熟悉?“Page not found”错误,“404 错误”一节也这样描述过。)

下一章将要讲?

我们现在已经学会了怎么在 Python 代码里硬编码 HTML 代码来处理视图。可惜的是,这种方法通常 不是一个好方法。幸运的是,Django 内建有一个简单有强大的模板处理引擎来让你分离两种工作:设计 HTML 页面和编写 Python 代码。下一章我们将 深入到 Django 的模板引擎里去。