目录
RequestContext 和上下文处理器
auth
debug
i18n
media
static
csrf
request
messages
自动转义 HTML
在单个变量中禁用
在模板中的块里禁用
首先,我们快速回顾一下前面介绍的几个术语:
• 模板是文本文档或普通的 Python 字符串,使用 Django 模板语言标记。模板中有模板标签和变量。
• 模板标签是模板中的一种符号,用于做特定的事情。这样定义相当晦涩。我们来举些例子:模板标签
可以生成内容、用做控制结构( if 语句或 for 循环)、从数据库中获取内容,或者访问其他模板标
签。模板标签放在 {% 和 %} 之间:
{% if is_logged_in %}
Thanks for logging in!
{% else %}
Please log in.
{% endif %}
• 变量也是模板中的一种符号,用于输出值。变量放在 {{ 和 }} 之间:
My first name is {{ first_name }}. My last name is {{ last_name }}.
• 上下文是传给模板的名值映射(类似于 Python 字典)。
• 模板渲染上下文的过程是把变量所在的位置替换成上下文中的值,并执行所有模板标签。
来看下面两个视图:
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)
在这两个视图中,我们向模板传入了相同的三个变量: app 、 user 和 ip_address 。如果能去掉这些重复不是更好吗? RequestContext 和上下文处理器就是为解决这种问题而出现的。上下文处理器用于指定自动在各个上下文中设定的变量,这样就无需每次调用 render() 时都指定。
为此,渲染模板时要把 Context 换成 RequestContext 。使用上下文处理器最低层的做法是创建一些处理器,将其传给 RequestContext 。使用上下文处理器改写上述示例得到的代码如下:
from django.template import loader, RequestContext
def custom_proc(request):
# 一个上下文处理器,提供 'app'、'user' 和 '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 。这是一个上下文处理器,它的参数是一个 HttpRequest 对象,返回值是一个字典,包含要在模板上下文中使用的变量。就这么简单。
• 我们修改了那两个视图函数,把 Context 换成 RequestContext 。构建这种上下文的方式有两处不同:其一, RequestContext 要求第一个参数必须是一个 HttpRequest 对象,即传给视图函数的那个参数( request );其二, RequestContext 接受可选的 processors 参数,其值是要使用的上下文处理器列表或元组。这里,我们传入的是前面定义的那个处理器 custom_proc 。
• 现在,各个视图在构建上下文时无需包含 app 、 user 或 ip_address ,因为 custom_proc 已经提供。
• 如果需要,视图仍可以提供其他模板变量。这里,我们为两个视图设定了不同的 message 模板变量。
为了说明上下文处理器的低层工作方式,上述示例没有使用 render() 快捷方式。但是,使用上下文处理器时可以这么做,也推荐这么做。为此,要使用 context_instance 参数,如下所示
from django.shortcuts import render
from django.template import RequestContext
def custom_proc(request):
# 一个上下文处理器,提供 'app'、'user' 和 'ip_address'
return {
'app': 'My app',
'user': request.user,
'ip_address': request.META['REMOTE_ADDR']
}
def view_1(request):
# ...
return render(request, 'template1.html',
{'message': 'I am view 1.'},
context_instance=RequestContext(request, processors=[custom_proc])
)
def view_2(request):
# ...
return render(request, 'template2.html',
{'message': 'I am the second view.'},
context_instance=RequestContext(request, processors=[custom_proc])
)
我们去掉了数据(模板变量)中的重复,但是增加了代码(调用processors 的代码)中的重复。
如果每一次都要输入 processors ,使用上下文处理器省不了多少事。鉴于此,Django 提供了全局上下文处理器。 context_processors 设置(在 settings.py 文件中)指明始终提供给 RequestContext 的上下文处理器。这样便不用每次使用 RequestContext 时都指定 processors 参数了。
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
这是一个可调用对象列表,接口与前文定义的 custom_proc 函数一样——参数是一个请求对象,返回值是一个字典,包含要合并到上下文中的变量。注意, context_processors 中的值是字符串,因此处理器要在Python 路径中(这样才能在设置中引用)。
指定的各个处理器按顺序运用。因此,如果一个处理器向上下文中添加了一个变量,而后面一个处理器又添加了一个同名变量,那么后一个将覆盖前一个。Django 提供了几个简单的上下文处理器,包含默认启用的那几个。下面几小节详述。
django.contrib.auth.context_processors.auth
启用这个处理器后, RequestContext 中将包含下述变量:
• user : auth.User 的实例,表示当前登录的用户(如未登录,是 AnonymousUser 实例)。
• perms : django.contrib.auth.context_processors.PermWrapper 实例,表示当前登录用户拥有的权限。
django.template.context_processors.debug
启用这个处理器后, RequestContext 中将包含下面两个变量,但前提是 DEBUG 设置的值是 True ,而且 INTER-
NAL_IPS 设置中包含请求的 IP 地址( request.META['REMOTE_ADDR'] ):
• debug : True 。可以在模板中测试是否在 DEBUG 模式中。
• sql_queries : {'sql': …, 'time': …} 字典构成的列表,表示处理请求的过程中执行的 SQL 查询及其
用时。列表中的值按查询的执行顺序排列,在访问时惰性生成
django.template.context_processors.i18n
启用这个处理器后, RequestContext 中将包含下面两个变量:
• LANGUAGES : LANGUAGES 设置的值。
• LANGUAGE_CODE :如果 request.LANGUAGE_CODE 存在,返回它的值;否则返回 LANGUAGE_CODE 设置的值。
django.template.context_processors.media
启用这个处理器后, RequestContext 中将包含 MEDIA_URL 变量,提供 MEDIA_URL 设置的值。
django.template.context_processors.static
启用这个处理程序后, RequestContext 中将包含 STATIC_URL 变量,提供 STATIC_URL 设置的值。
django.template.context_processors.csrf
这个处理器添加一个令牌,供 csrf_token 模板标签使用,用于防范跨站请求伪造
django.template.context_processors.request
启用这个处理器后, RequestContext 中将包含 request 变量,它的值是当前的 HttpRequest 对象。
django.contrib.messages.context_processors.messages
启用这个处理器后, RequestContext 中将包含下面两个变量:
• messages :消息框架设定的消息列表(里面的值是字符串)。
• DEFAULT_MESSAGE_LEVELS :消息等级名称到数字值的映射。
使用模板生成 HTML 时,变量的值可能包含特殊的字符,对得到的 HTML 产生影响。对下述模板片段来说
渲染模板后得到的结果是:
Hello,
因此,浏览器会弹出一个对话框。
这种安全漏洞称为跨站脚本攻击(Cross Site Scripting,XSS)为了避免这种漏洞,有两个选择:
1. 每个不信任的变量都传给 escape 过滤器,把有潜在危害的 HTML 字符转换成无危害的。Django 起初的几年默认采用这种方案,它的问题是把责任强加到开发者或模板编写者身上了,你要确保转义一切。但是,忘记转义数据是常事。
2. 利用 Django 的自动转义 HTML 特性。
这个行为默认已启用。如果使用 Django 的模板系统,就能受到这一措施的保护。
若想在单个变量中禁用自动转义,使用 safe 过滤器:
This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}
这个过滤器的作用可以理解为“无需转义,可以放心使用”,或者“可以安全解释为 HTML”。这里,如果 data
中包含 ' ' ,得到的结果为:
This will be escaped: <b>
This will not be escaped:
若想在模板中控制自动转义行为,把模板(或其中一部分)放在 autoescape 标签里,如下所示:
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
autoescape 标签的参数为 on 或 off 。off 即是关闭有时需要强制自动转义,有时则想禁用。下面举个例子:
Auto-escaping is on by default. Hello {{ name }}
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}