RequestContext和Context处理器
你需要一段context来解析模板。 一般情况下,这是一个 django.template.Context
的实例,不过在Django中还可以用一个特殊的子类, django.template.RequestContext
,这个用起来稍微有些不同。 RequestContext
默认地在模板context中加入了一些变量,如 HttpRequest
对象或当前登录用户的相关信息。
当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 RequestContext
。 例如,考虑这两个视图:
from django.template import loader, Context
def view_1(request):
# ...
t = loader.get_template('template1.html')
c = Context({
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR'],
'message': 'I am view 1.'
})
return t.render(c)
def view_2(request):
# ...
t = loader.get_template('template2.html')
c = Context({
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR'],
'message': 'I am the second view.'
})
return t.render(c)
(注意,在这些例子中,我们故意 不 使用 render_to_response()
这个快捷方法,而选择手动载入模板,手动构造context对象然后渲染模板。 是为了能够清晰的说明所有步骤。)
每个视图都给模板传入了三个相同的变量:app
、user
和ip_address
。 如果我们把这些冗余去掉会不会更好?
创建 RequestContext
和 context处理器 就是为了解决这个问题。 Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用 render_to_response()
时都指定。 要点就是,当你渲染模板时,你要用 RequestContext
而不是 Context
。
最直接的做法是用context处理器来创建一些处理器并传递给 RequestContext
。上面的例子可以用context processors改写如下:
from django.template import loader, RequestContext
def custom_proc(request):
"A context processor that provides 'app', 'user' and 'ip_address'."
return {
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR']
}
def view_1(request):
# ...
t = loader.get_template('template1.html')
c = RequestContext(request, {'message': 'I am view 1.'},
processors=[custom_proc])
return t.render(c)
def view_2(request):
# ...
t = loader.get_template('template2.html')
c = RequestContext(request, {'message': 'I am the second view.'},
processors=[custom_proc])
return t.render(c)
我们来通读一下代码:
首先,我们定义一个函数
custom_proc
。这是一个context处理器,它接收一个HttpRequest
对象,然后返回一个字典,这个字典中包含了可以在模板context中使用的变量。 它就做了这么多。我们在这两个视图函数中用
RequestContext
代替了Context
。在context对象的构建上有两个不同点。 一,RequestContext
的第一个参数需要传递一个HttpRequest
对象,就是传递给视图函数的第一个参数(request
)。二,RequestContext
有一个可选的参数processors
,这是一个包含context处理器函数的列表或者元组。 在这里,我们传递了我们之前定义的处理器函数curstom_proc
。每个视图的context结构里不再包含
app
、user
、ip_address
等变量,因为这些由custom_proc
函数提供了。每个视图 仍然 具有很大的灵活性,可以引入我们需要的任何模板变量。 在这个例子中,
message
模板变量在每个视图中都不一样。
在第四章,我们介绍了 render_to_response()
这个快捷方式,它可以简化调用 loader.get_template()
,然后创建一个 Context
对象,最后再调用模板对象的 render()
过程。 为了讲解context处理器底层是如何工作的,在上面的例子中我们没有使用 render_to_response()
。但是建议选择 render_to_response()
作为context的处理器。这就需要用到context_instance
参数:
from django.shortcuts import render_to_response
from django.template import RequestContext
def custom_proc(request):
"A context processor that provides 'app', 'user' and 'ip_address'."
return {
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR']
}
def view_1(request):
# ...
return render_to_response('template1.html',
{'message': 'I am view 1.'},
context_instance=RequestContext(request, processors=[custom_proc]))
def view_2(request):
# ...
return render_to_response('template2.html',
{'message': 'I am the second view.'},
context_instance=RequestContext(request, processors=[custom_proc]))
在这,我们将每个视图的模板渲染代码写成了一个单行。
虽然这是一种改进,但是,请考虑一下这段代码的简洁性,我们现在不得不承认的是在 另外 一方面有些过分了。 我们以代码冗余(在 processors
调用中)的代价消除了数据上的冗余(我们的模板变量)。 由于你不得不一直键入 processors
,所以使用context处理器并没有减少太多的输入量。
Django因此提供对 全局 context处理器的支持。 TEMPLATECONTEXT_PROCESSORS
指定了哪些context processors
总是_默认被使用。这样就省去了每次使用 RequestContext
都指定 processors
的麻烦。
默认情况下, TEMPLATE_CONTEXT_PROCESSORS
设置如下:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
)
这个设置项是一个可调用函数的元组,其中的每个函数使用了和上文中我们的 custom_proc
相同的接口,它们以request对象作为参数,返回一个会被合并传给context的字典: 接收一个request对象作为参数,返回一个包含了将被合并到context中的项的字典。
每个处理器将会按照顺序应用。 也就是说如果你在第一个处理器里面向context添加了一个变量,而第二个处理器添加了同样名字的变量,那么第二个将会覆盖第一个。
Django提供了几个简单的context处理器,有些在默认情况下被启用的。
django.core.context_processors.auth
如果 TEMPLATE_CONTEXT_PROCESSORS
包含了这个处理器,那么每个 RequestContext
将包含这些变量:
user
:一个django.contrib.auth.models.User
实例,描述了当前登录用户(或者一个AnonymousUser
实例,如果客户端没有登录)。messages
:一个当前登录用户的消息列表(字符串)。 在后台,对每一个请求,这个变量都调用request.user.get_and_delete_messages()
方法。 这个方法收集用户的消息然后把它们从数据库中删除。perms
:django.core.context_processors.PermWrapper
的一个实例,包含了当前登录用户有哪些权限。
关于users、permissions和messages的更多内容请参考第14章。
django.core.context_processors.debug
这个处理器把调试信息发送到模板层。 如果TEMPLATE_CONTEXT_PROCESSORS
包含这个处理器,每一个RequestContext
将包含这些变量:
debug
:你设置的DEBUG
的值(True
或False
)。你可以在模板里面用这个变量测试是否处在debug模式下。sql_queries
:包含类似于 ``{‘sql’: …, ‘time’: `` 的字典的一个列表, 记录了这个请求期间的每个SQL查询以及查询所耗费的时间。 这个列表是按照请求顺序进行排列的。
System Message: WARNING/2 (<string>
, line 315); backlink
Inline literal start-string without end-string.
由于调试信息比较敏感,所以这个context处理器只有当同时满足下面两个条件的时候才有效:
DEBUG
参数设置为True
。请求的ip应该包含在
INTERNAL_IPS
的设置里面。
细心的读者可能会注意到debug
模板变量的值永远不可能为False
,因为如果DEBUG
是False
,那么debug
模板变量一开始就不会被RequestContext所包含。
django.core.context_processors.i18n
如果这个处理器启用,每个 RequestContext
将包含下面的变量:
LANGUAGES
:LANGUAGES
选项的值。LANGUAGE_CODE
:如果request.LANGUAGE_CODE
存在,就等于它;否则,等同于LANGUAGE_CODE
设置。
附录E提供了有关这两个设置的更多的信息。
django.core.context_processors.request
如果启用这个处理器,每个 RequestContext
将包含变量 request
, 也就是当前的 HttpRequest
对象。 注意这个处理器默认是不启用的,你需要激活它。
如果你发现你的模板需要访问当前的HttpRequest
你就需要使用它:
{{ request.REMOTE_ADDR }}
写Context处理器的一些建议
编写处理器的一些建议:
使每个context处理器完成尽可能小的功能。 使用多个处理器是很容易的,所以你可以根据逻辑块来分解功能以便将来复用。
要注意
TEMPLATECONTEXT_PROCESSORS
里的context processor 将会在基于这个settings.py的每个_ 模板中有效,所以变量的命名不要和模板的变量冲突。 变量名是大小写敏感的,所以processor的变量全用大写是个不错的主意。不论它们存放在哪个物理路径下,只要在你的Python搜索路径中,你就可以在
TEMPLATE_CONTEXT_PROCESSORS
设置里指向它们。 建议你把它们放在应用或者工程目录下名为context_processors.py
的文件里。