Django使用request和response对象在系统间传递状态。
当一个页面被请示时,Django创建一个包含请求元数据的 HttpRequest 对象。 然后Django调入合适的视图,把 HttpRequest 作为视图函数的第一个参数 传入。每个视图要负责返回一个 HttpResponse 对象。
我们在书中已经使用过这些对象了;这篇附录说明了 HttpRequest 和 HttpResponse 的全部API。
HttpRequest 表示来自某客户端的一个单独的HTTP请求。
HttpRequest实例的属性包含了关于此次请求的大多数重要信息(详见表H-1)。 除了session外的所有属性都应该认为是只读的.
属性 | 描述 |
---|---|
path | 表示提交请求页面完整地址的字符串, 不包括域名,如 "/music/bands/the_beatles/" 。 |
method | 表示提交请求使用的HTTP方法。 它总是大写的。例如: if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else() |
GET | 一个类字典对象,包含所有的HTTP的GET参数的信息。 见 QueryDict 文档。 |
POST | 一个类字典对象,包含所有的HTTP的POST参数的信息。 见 QueryDict 文档。 通过POST提交的请求有可能包含一个空的 POST 字典, 也就是说, 一个通过POST方法提交的表单可能不包含数据。 因此,不应该使用 if request.POST 来判断POST方法的使用, 而是使用 if request.method == "POST" (见表中的 method 条目)。 注意: POST 并 不 包含文件上传信息。 见 FILES 。 |
REQUEST | 为了方便而创建,这是一个类字典对象,先搜索 POST , 再搜索 GET 。 灵感来自于PHP的 $_REQEUST 。 例如, 若 GET = {"name": "john"} , POST = {"age": '34'} , REQUEST["name"] 会是 "john" , REQUEST["age"] 会是 "34" 。 强烈建议使用 GET 和 POST ,而不是 REQUEST 。 这是为了向前兼容和更清楚的表示。 |
COOKIES | 一个标准的Python字典,包含所有cookie。 键和值都是字符串。cookie使用的更多信息见第12章。 |
FILES | 一个类字典对象,包含所有上传的文件。 FILES 的键来自 <input type="file" name="" /> 中的 name 。 FILES 的值是一个标准的Python字典, 包含以下三个键:
注意 FILES 只在请求的方法是 POST ,并且提交的 <form> 包含 enctype="multipart/form-data" 时 才包含数据。否则, FILES 只是一个空的类字典对象。 |
META | 一个标准的Python字典,包含所有有效的HTTP头信息。 有效的头信息与客户端和服务器有关。 这里有几个例子:
在 META 中有效的任一HTTP头信息都是带有 HTTP_ 前缀的 键,例如:
|
user | 一个 django.contrib.auth.models.User 对象表示 当前登录用户。 若当前用户尚未登录, user 会设为 django.contrib.auth.models.AnonymousUser 的一个实例。 可以将它们与 is_authenticated() 区别开: if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. user 仅当Django激活 AuthenticationMiddleware 时有效。 关于认证和用户的完整细节,见第12章。 |
session | 一个可读写的类字典对象,表示当前session。 仅当Django已激活session支持时有效。 见第12章。 |
raw_post_data | POST的原始数据。 用于对数据的复杂处理。 |
Request对象同样包含了一些有用的方法,见表H-2。
方法 | 描述 |
---|---|
__getitem__(key) | 请求所给键的GET/POST值,先查找POST,然后是GET。 若键不存在,则引发异常 KeyError 。 该方法使用户可以以访问字典的方式来访问一个 HttpRequest 实例。 例如, request["foo"] 和先检查 request.POST["foo"] 再检查 request.GET["foo"] 一 样。 |
has_key() | 返回 True 或 False , 标识 request.GET 或 request.POST 是否包含所给的 键。 |
get_full_path() | 返回 path ,若请求字符串有效,则附加于其后。 例如, "/music/bands/the_beatles/?print=true" 。 |
is_secure() | 如果请求是安全的,则返回 True 。 也就是说,请求是以HTTPS的形式提交的。 |
在一个 HttpRequest 对象中, GET 和 POST 属性都是 django.http.QueryDict 的实例。 QueryDict 是一个类似于字典的类,专门用来处理用一个键的多值。当处理一些HTML表单中的元素,特别是 <select multiple="multiple"> 之类传递同一key的多值的元素时,就需要这个类了。
QueryDict 实例是不可变的,除非创建了一个 copy() 副本。也就是说不能直接更改 request.POST 和 request.GET 的属性。
QueryDict 实现了所有标准的字典的方法,因为它正是字典的一个子类。与其不同的东西都已在表H-3中列出。
方法 | 与标准字典实现的不同 |
---|---|
__getitem__ | 与一个字典一样。但是,当一个键有多个值时, __getitem__() 返回最后一个值。 |
__setitem__ | 将所给键的值设为 [value] (一个只有一个 value 元素的 Python列表)。 注意,因对其它的字典函数有副作用,故它只能被称 为一个可变的 QueryDict (通过 copy() 创建)。 |
get() | 如果一个键多个值,和 __getitem__ 一样, get() 返回 最后一个值。 |
update() |
>>> q = QueryDict('a=1') >>> q = q.copy() # 使其可变 >>> q.update({'a': '2'}) >>> q.getlist('a') ['1', '2'] >>> q['a'] # 返回最后一个值 ['2'] |
items() | 和标准字典的 items() 方法一样, 不同的是它和 __getitem()__ 一样,返回最后一个值: >>> q = QueryDict('a=1&a=2&a=3') >>> q.items() [('a', '3')] |
values() | 和标准字典的 values() 方法一样, 不同的是它和 __getitem()__ 一样,返回最后一个值。 |
另外, QueryDict 还有在表H-4中列出的方法。
方法 | 描述 |
---|---|
copy() | 返回一个对象的副本,使用的是Python标准库中的 copy.deepcopy() 。 该副本是可变的, 也就是说,你能改变它的值。 |
getlist(key) | 以Python列表的形式返回所请求键的数据。 若键不存在则返回空列表。 它保证了一定会返回某种形式的list。 |
setlist(key, list_) | 将所给键的键值设为 list_ (与 __setitem__() 不同)。 |
appendlist(key, item) | 在 key 相关的list上增加 item 。 |
setlistdefault(key, l) | 和 setdefault 一样, 不同的是它的第二个参数是 一个列表,而不是一个值。 |
lists() | 和 items() 一样, 不同的是它以一个列表的形式 返回字典每一个成员的所有值。 例如: >>> q = QueryDict('a=1&a=2&a=3') >>> q.lists() [('a', ['1', '2', '3'])] |
urlencode() | 返回一个请求字符串格式的数据字符串 (如, "a=2&b=3&b=5" )。 |
例如, 给定这个HTML表单:
<form action="/foo/bar/" method="post"> <input type="text" name="your_name" /> <select multiple="multiple" name="bands"> <option value="beatles">The Beatles</option> <option value="who">The Who</option> <option value="zombies">The Zombies</option> </select> <input type="submit" /> </form>
如果用户在 your_name 中输入 "John Smith" ,并且在多选框中同时选择了The Beatles和The Zombies,那么以下就是Django的request对象所拥有的:
>>> request.GET {} >>> request.POST {'your_name': ['John Smith'], 'bands': ['beatles', 'zombies']} >>> request.POST['your_name'] 'John Smith' >>> request.POST['bands'] 'zombies' >>> request.POST.getlist('bands') ['beatles', 'zombies'] >>> request.POST.get('your_name', 'Adrian') 'John Smith' >>> request.POST.get('nonexistent_field', 'Nowhere Man') 'Nowhere Man'
使用时请注意:
GET , POST , COOKIES , FILES , META , REQUEST , raw_post_data 和 user 这些属性都是延迟加载的。 也就是说除非代码中访问它们,否则Django并不会花费资源来计算这些属性值。
与Django自动创建的 HttpRequest 对象相比, HttpResponse 对象则是由你创建的。 你创建的每个视图都需要实例化,处理和返回一个 HttpResponse 对象。
HttpResponse 类存在于 django.http.HttpResponse 。
一般情况下,你创建一个 HttpResponse 时,以字符串的形式来传递页面的内容给 HttpResponse 的构造函数:
>>> response = HttpResponse("Here's the text of the Web page.") >>> response = HttpResponse("Text only, please.", mimetype="text/plain")
但是如果希望逐渐增加内容,则可以把 response 当作一个类文件对象使用:
>>> response = HttpResponse() >>> response.write("<p>Here's the text of the Web page.</p>") >>> response.write("<p>Here's another paragraph.</p>")
你可以将一个迭代器传递给 HttpResponse ,而不是固定的字符串。如果你要这样做的话,请遵循以下规则:
迭代器应返回字符串。
若一个 HttpResponse 已经通过实例化,并以一个迭代器作为其内容,就不能以一个类文件对象使用 HttpResponse 实例。这样做的话,会导致一个 Exception 。
最后,注意 HttpResponse 实现了一个 write() 方法,使其可以在任何可以使用类文件对象的地方使用。 这方面的例子见第11章。
您可以使用字典一样地添加和删除头信息。
>>> response = HttpResponse() >>> response['X-DJANGO'] = "It's the best." >>> del response['X-PHP'] >>> response['X-DJANGO'] "It's the best."
你也可以使用 has_header(header) 来检查一个头信息项是否存在。
请避免手工设置 Cookie 头,参见第12章Django中cookie工作原理的说明。
Django包含许多处理不同类型的HTTP请求的 HttpResponse 子类(见表H-5)。像 HttpResponse 一样,这些类在 django.http 中。
当然,如果框架不支持一些特性,你也可以定义自己的 HttpResponse 子类来处理不同的请求。
在Django中返回HTTP错误代码很容易。我们前面已经提到 HttpResponseNotFound , HttpResponseForbidden , HttpResponseServerError ,和其它子类。为了更好地表示一个错误,只要返回这些子类之一的一个实例,而不是一个通常的 HttpResponse ,例如:
def my_view(request): # ... if foo: return HttpResponseNotFound('<h1>Page not found</h1>') else: return HttpResponse('<h1>Page was found</h1>')
至今为止,404错误是最常见的HTTP错误,有一种更容易的方式来处理。
当返回一个错误,比如 HttpResponseNotFound 时,需要定义错误页面的HTML:
return HttpResponseNotFound('<h1>Page not found</h1>')
为了方便,而且定义一个通用的应用于网站的404错误页面也是一个很好的选择,Django提供了一个 Http404 异常。如果在视图的任何地方引发 Http404 异常,Django就会捕获错误并返回应用程序的标准错误页面,当然,还有HTTP错误代码404。
例如:
from django.http import Http404 def detail(request, poll_id): try: p = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404 return render_to_response('polls/detail.html', {'poll': p})
为了完全发挥出 Http404 的功能,应创建一个模板,在404错误被引发时显示。模板的名字应该是 404.html ,而且应该位于模板树的最高层。
当引发 Http404 异常,Django加载一个专门处理404错误的视图。默认情况下,这个视图是 django.views.defaults.page_not_found ,它会加载并显示模板 404.html 。
这意味着需要在根模板目录定义一个 404.html 模板。这个模板会作用于所有404错误。
视图 page_not_found 适用于99%的网站应用程序,但若是希望重载该视图,可以在URLconf中指定 handler404 ,就像这样:sfas
from django.conf.urls.defaults import * urlpatterns = patterns('', ... ) handler404 = 'mysite.views.my_custom_404_view'
后台执行时,Django以 handler404 来确定404视图。默认情况下,URLconf包含以下内容:
from django.conf.urls.defaults import *
这句话负责当前模块中的 handler404 设置。正如你所见,在 django/conf/urls/defaults.py 中, handler404 默认被设为 'django.views.defaults.page_not_found' 。
关于404视图,有三点需要注意:
当Django在URLconf无法找到匹配的正则表达式时,404视图会显示。
如果没有定义自己的404视图,而只是简单地使用默认的视图,此时就需要在模板目录的根目录创建一个 404.html 模板。默认的404视图会对所有404错误使用改模板。
若 DEBUG 被设为 True (在settings模块内),则404视图不会被使用,此时显示的是跟踪信息。
同样地,若是在试图代码中出现了运行时错误,Django会进行特殊情况处理。如果视图引发了一个异常,Django会默认访问视图 django.views.defaults.server_error ,加载并显示模板 500.html 。
这意味着需要在根模板目录定义一个 500.html 模板。该模板作用于所有服务器错误。
视图 server_error 适用于99%的网站应用程序,但若是希望重载该视图,可以在URLconf中指定 handler500 ,就像这样:
from django.conf.urls.defaults import * urlpatterns = patterns('', ... ) handler500 = 'mysite.views.my_custom_error_view'
关于本评注系统
本站使用上下文关联的评注系统来收集反馈信息。不同于一般对整章做评注的做法, 我们允许你对每一个独立的“文本块”做评注。一个“文本块”看起来是这样的:
一个“文本块”是一个段落,一个列表项,一段代码,或者其他一小段内容。 你选中它会高亮度显示:
要对文本块做评注,你只需要点击它旁边的标识块:
我们会仔细阅读每个评论,如果可能的话我们也会把评注考虑到未来的版本中去:
如果你愿意你的评注被采用,请确保留下你的全名 (注意不是昵称或简称)
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.ext
library. Thanks also to Yahoo for YUI itself.