详解Django框架中用context来解析模板的方法
你需要一段context来解析模板。一般情况下,这是一个django.template.Context的实例,不过在Django中还可以用一个特殊的子类,django.template.RequestContext,这个用起来稍微有些不同。RequestContext默认地在模板context中加入了一些变量,如HttpRequest对象或当前登录用户的相关信息。
当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用RequestContext。例如,考虑这两个视图:
fromdjango.templateimportloader,Context defview_1(request): #... t=loader.get_template('template1.html') c=Context({ 'app':'Myapp', 'user':request.user, 'ip_address':request.META['REMOTE_ADDR'], 'message':'Iamview1.' }) returnt.render(c) defview_2(request): #... t=loader.get_template('template2.html') c=Context({ 'app':'Myapp', 'user':request.user, 'ip_address':request.META['REMOTE_ADDR'], 'message':'Iamthesecondview.' }) returnt.render(c)
(注意,在这些例子中,我们故意不使用render_to_response()这个快捷方法,而选择手动载入模板,手动构造context对象然后渲染模板。是为了能够清晰的说明所有步骤。)
每个视图都给模板传入了三个相同的变量:app、user和ip_address。如果我们把这些冗余去掉会不会更好?
创建RequestContext和context处理器就是为了解决这个问题。Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用render_to_response()时都指定。要点就是,当你渲染模板时,你要用RequestContext而不是Context。
最直接的做法是用context处理器来创建一些处理器并传递给RequestContext。上面的例子可以用contextprocessors改写如下:
fromdjango.templateimportloader,RequestContext defcustom_proc(request): "Acontextprocessorthatprovides'app','user'and'ip_address'." return{ 'app':'Myapp', 'user':request.user, 'ip_address':request.META['REMOTE_ADDR'] } defview_1(request): #... t=loader.get_template('template1.html') c=RequestContext(request,{'message':'Iamview1.'}, processors=[custom_proc]) returnt.render(c) defview_2(request): #... t=loader.get_template('template2.html') c=RequestContext(request,{'message':'Iamthesecondview.'}, processors=[custom_proc]) returnt.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模板变量在每个视图中都不一样。
为了讲解context处理器底层是如何工作的,在上面的例子中我们没有使用render_to_response()。但是建议选择render_to_response()作为context的处理器。这就需要用到context_instance参数:
fromdjango.shortcutsimportrender_to_response fromdjango.templateimportRequestContext defcustom_proc(request): "Acontextprocessorthatprovides'app','user'and'ip_address'." return{ 'app':'Myapp', 'user':request.user, 'ip_address':request.META['REMOTE_ADDR'] } defview_1(request): #... returnrender_to_response('template1.html', {'message':'Iamview1.'}, context_instance=RequestContext(request,processors=[custom_proc])) defview_2(request): #... returnrender_to_response('template2.html', {'message':'Iamthesecondview.'}, context_instance=RequestContext(request,processors=[custom_proc]))
在这,我们将每个视图的模板渲染代码写成了一个单行。
虽然这是一种改进,但是,请考虑一下这段代码的简洁性,我们现在不得不承认的是在另外一方面有些过分了。我们以代码冗余(在processors调用中)的代价消除了数据上的冗余(我们的模板变量)。由于你不得不一直键入processors,所以使用context处理器并没有减少太多的输入量。
Django因此提供对全局context处理器的支持。TEMPLATE_CONTEXT_PROCESSORS指定了哪些contextprocessors总是默认被使用。这样就省去了每次使用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查询以及查询所耗费的时间。这个列表是按照请求顺序进行排列的。
- SystemMessage:WARNING/2(<string>,line315);backlink
- Inlineliteralstart-stringwithoutend-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设置。
django.core.context_processors.request
如果启用这个处理器,每个RequestContext将包含变量request,也就是当前的HttpRequest对象。注意这个处理器默认是不启用的,你需要激活它。
如果你发现你的模板需要访问当前的HttpRequest你就需要使用它:
{{request.REMOTE_ADDR}}