模板语言术语:
- 模板:是文本文档或普通的python字符串,使用django模块语言标记。模块中有模板标签和变量。
- 模板标签:是模板中的一种符号,用于做特定的事情。模板表情可以生成内容,用作控制结构(if语句或for循环)、从数据中获取内容,或访问其他模板标签。模板标签放在{% 和%}之间。
{% if is_logged_in %}
Thank for logging in!
{% else %}
Please log in.
{% endif %}
- 变量:是模板中一个符号,用于输出值。变量放在{{和}}之间: My first name is {{first_name}} . My last name is {{ last_name }}.
- 上下文:是传给模板的名值映射(类似于Python字典)。模板渲染上线我的过程是把变量所在的位置替换成上下文中的值,并执行所有模板表情。
RequestContext和上下文处理器
模板要在上下文中渲染。上下文是django.template.Context 的实例,不过django还提供了一个子类,django.template.RequestContext。
from django.template import loader, Context def template1(request): t = loader.get_template('template1.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip address': request.META['REMOTE_ADDR'], 'message': 'I am template1.' }) return t.render(c) def template2(request): t = loader.get_template('template2.html') c = Context({ 'app': 'My app', 'user': request.user, 'ip address': request.META['REMOTE_ADDR'], 'message': 'I am template2.' }) return t.render(c)
在两个视图中,我们想模板传入了相同的三个变量:app、user和ip_address。如果能去掉这些重复不是更好吗?RequestContext和上下文处理器就是为解决这种问题而出现的。上下文处理器用于指定自动在各个上下文中设定的变量,这样无需每次调用然的人()时都指定。
为此渲染模块是把Context换成RequestContext。使用上下文处理器最底层的做法是创建一些处理器,将其传给RequestContext。使用上下文处理器改写上述示例得到的代码如下:
1 from django.template import loader, Context,RequestContext 2 3 def custom_proc(request): 4 # 一个上下文处理器,提供'app'、'user'和'ip_address' 5 return { 6 'app': 'My app', 7 'user': request.user, 8 'ip address': request.META['REMOTE_ADDR'], 9 } 10 11 def template1(request): 12 t = loader.get_template('template1.html') 13 c = RequestContext(request, 14 { 'message': 'I am template1.'}, 15 processors=[custom_proc] 16 ) 17 return t.render(c) 18 19 20 def template2(request): 21 t = loader.get_template('template2.html') 22 23 c = RequestContext(request, 24 {'message': 'I am template2.'}, 25 processors=[custom_proc]) 26 return t.render(c)
代码分析:
-
- 首先,定义函数custom_proc。这是一个上下文处理器,它的参数是一个HttpRequest对象,返回值是一个字典,包含要在模板上下文中使用的变量。
- 我们修改了那两个视图函数,把Context换成RequestContext。构建这种上下文的方式有两处不同:其一,RequestContext要求第一个参数必须是一个HttpRequest对象,即传给视图函数的那个参数(request);其二,RequestContext接受可选的processors参数,其值是要使用的上下文处理器列表或元组。这里,我们传入的是前面定义的那个处理器custom_proc。
- 现在,各个视图在构建上下文是无需包含app、user或ip_address,因为custom_proc已经提供。
自定义上下文处理器的指导方针
上下文处理器的接口十分简单,它就是普通的Python函数,有一个参数,是一个HttpRequest对象,返回一个字典,用于添加到模板上下文中。上下文处理器必须返回一个字典。一下是自定义处理器的几个小贴士:
- 上下文处理器应该尽量负责较少的功能。处理器易于何用,因此应该把该功能分成逻辑片段,供以后复用。
- 记住,TEMPLATE_CONTEXT_PROCESSORS中列出的各个上下文处理器在那个设置文件管辖的每个模板中都可用,因此应该为变量挑选不易与模板自身设置的变量冲突的名称。因为变量却分大小写,所以让处理器提供的变量都使用大写不失为一个好主意。
- 自定义的上下文处理器可以放在代码基的任何位置。Django只关系TEMPLATES设置中的‘context_proecssors’选项或者Engine的context_processors参数(直接使用Engine时)有没有指向你的上下文处理器。尽管入职,约定的做法是把上下文处理器保存在应用或项目中一个名为context_precessor.py的文件中。
自动转义html
跨站脚本攻击(Cross Site Scripting,XSS):XSS是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。为了避免这种漏洞,有两个选择:
- 每个不信任的变量都传给escape过滤器,把有潜在危害的html自渡船转换成无危害的; 利用Django的自动转义html特性,默认转义模板中的每个变量表签;
- <转换成<
- >转换成>
- ‘(单引号)转换成'
- “(双引号)转换成"
- &转换成&
自动转义可以在整站禁用、在模板层禁用或在变量层禁用。
- 在单个变量中禁用
若想在单个变量中禁用自动转义,使用safe过滤器:
This will be escaped: {{data}}
This will be escaped: {{data|safe}}
这个过滤器的作用可以理解为“无需转义,可以放心使用”,或者“可以安全解释为HTML”。这里如果data中包含'<b>',得到结果为:
This will be escaped: <b>
This will be escaped: <b>
- 在模板中的块里禁用
如想在模板中控制自动转义行为,把模板(或其中一部分)放在autoescape标签里,如下
{% autoescape off %}
Hello {{ name }}
{% endautoescape %}
autoescape标签的参数为on或off。有时需要强制自动转义,有时则想禁用
{% autoescape off %}
This will not be auto-escaped: {{ data }}.
Nor this: {{ other_data }}
Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}
自定义模板标签和过滤器
Django的模板语言自带了丰富的标签和过滤器,能满足常见的表现逻辑需求。但是,这些内建的标签和过滤器可能缺少我们需要的功能。
- 自定义模板过滤器:自定义的过滤器其实就是普通的Python函数,接受一个或多个参数
-
- 变量的值(输入),不一定是字符串
- 参数的值,可以有默认值,也可以留空
2. 注册自定义的过滤器
定义好过滤器之后,要使用Library实例注册,让django的模板语言知道他的存在:
register.filter('cut', cut)
register.filter('lower', lower)
Library.filter()有两个参数:
1、过滤器的名称,一个字符串
2、负责处理过滤器的函数,一个Python函数(不是函数名称的字符串形式)。
3. 过滤器和自动转义:
-
- 原始字符串是原始的Python str或unicode类型。输出是,如果启用了自定义转义就转义,否则原封不动呈现出来。
- 安全字符串是标记为安全的字符串,输出时不会转义,因为已经做了必要的转义。在客户端需要封装不动输出原始HTML时经常使用这种字符串。
在内部这种字符串是SafeBytes或SafeText类型。二者的共同基类是SafeData,因此可以使用类似下面的代码测试:
if isinstance(value,SafeData)
-
- 标记为“需要转义”的自渡船是在输出时时钟应该转义的字符串,不管在不在autoescape块中都是如此。
4.模板过滤分属两种情况:
-
- 不再尚未呈现的结果中引入对HTML不安全的字符(<、>、'、"或&);
- 过滤代码自行负责做必要的转义。在结果中引入新的HTML标记时必须这么做。
对第一种情况来说,可以交给Django的自动转义行为处理。只需在注册过滤器函数时把is_safe旗标设为True
@register.filter(is_safe=True)
def myfilter(value):
return value
这个旗标告诉Django,如果传入过去氯气的是“安全”字符串,结果仍是“安全的”。而如果传入不安全的字符串,必要时Django会自动转义。
is_safe的存在是有原因的,因为有相当多的字符串操作会把SafeData对象便会普通的str或unicode对象,而捕获所有情况十分困难,Django会在过滤器执行完毕后修复损伤。
第二种情况没有把输出标记为安全的,HTML标记不会转义,因此你要自己动手处理。若想把输出标记为安全的字符串,使用django.utils.safestring.mark_safe()
自定义模板标签
简单的标签simple_tag
-
- 调用标签函数时已经检查了必要参数的数量,因此无需我们检查;
- 参数两侧的引号(如果有的话)已经去掉了,接受到的是普通的字符串;
- 如果参数是模板变量,产给标签汗湿的是变量的当前值,而不是变量本身。
注:本文章摘自《精通Django1.8LTS全解》第八章,未完待续