1.RequestContext和Context处理器
当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 RequestContext 。例如:
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 HttpResponse(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 HttpResponse(t.render(c))
RequestContext和Context在context对象的构建上有两个不同点。一, RequestContext 的第一个参数需要传递一个 HttpRequest 对象,就是传递给视图函数的第一个参数(request )。二, RequestContext 有一个可选的参数 processors ,这是一个包含context处理器函数的list或者tuple。
另外,Django还提供对 全局 context处理器的支持。 TEMPLATE_CONTEXT_PROCESSORS 指定了 总是 使用哪些 context processors 。这样就省去了每次使用 RequestContext 都指定 processors 的麻烦。例如:(/usr/lib/python2.6/site-packages/django/conf/global_settings.py)
TEMPLATE_CONTEXT_PROCESSORS = ( ‘django.contrib.auth.context_processors.auth‘, ‘django.core.context_processors.debug‘, ‘django.core.context_processors.i18n‘, ‘django.core.context_processors.media‘, ‘django.core.context_processors.static‘, ‘django.core.context_processors.tz‘, # ‘django.core.context_processors.request‘, ‘django.contrib.messages.context_processors.messages‘, )
2.html自动转义
从模板生成html的时候,总是有一个风险——变量包了含会影响结果html的字符。例如:
Hello, {{ name }}.
这看起来是显示用户名的一个无害的途径,但是考虑如果用户输入如下的名字将会发生什么:
<script>alert(‘hello‘)</script>
用这个用户名,模板将被翻译成:
Hello, <script>alert(‘hello‘)</script>
这意味着浏览器将弹出JavaScript警告框!
为了避免这个问题,你有两个选择:
一是你可以确保每一个不被信任的变量通过过滤器,它把潜在有害的html字符转换为无害的。
二是,你可以利用django的自动html转意。
在django里默认情况下,每一个模板自动转意每一个变量标签的输出。 尤其是这五个字符。
< 被转意为 <
> 被转意为 >
‘ (single quote) 被转意为 '
" (double quote) 被转意为 "
& 被转意为 &
注:自动转义默认是开启的。 如果你正在使用django的模板系统,那么你是被保护的。
3.如何关闭自动转义
1).用safe过滤器为单独的变量关闭自动转意:
This will be escaped: {{ data }} This will not be escaped: {{ data|safe }}
2).为了控制模板的自动转意,用标签autoescape来包装整个模板:
{% autoescape off %} Hello {{ name }} {% endautoescape %}
注:autoescape 标签有两个参数on和off
3.扩展模板系统
创建一个模板库:不管是写自定义标签还是过滤器,第一件要做的事是创建模板库(Django能够导入的基本结构)。
创建一个模板库分两步走:
第一,决定哪个Django应用应当拥有这个模板库。如果你通过 manage.py startapp 创建了一个应用,你可以把它放在那里,或者你可以为模板库单独创建一个应用。无论你采用何种方式,请确保把你的应用添加到 INSTALLED_APPS 中。
第二,在适当的Django应用包里创建一个 templatetags 目录。 这个目录应当和 models.py 、 views.py 等处于同一层次。在 templatetags 中创建两个空文件: 一个 __init__.py (告诉Python这是
一个包含了Python代码的包)和一个用来存放你自定义的标签/过滤器定义的文件。 第二个文件 的名字稍后将用来加载标签。例如,如果你的自定义标签/过滤器在一个叫作 poll_extras.py 的文件中,你需要在模板中写入如下内容:
{% load poll_extras %}
注:{% load %} 标签检查 INSTALLED_APPS 中的设置,仅允许加载已安装的Django应用程序中的模板库。
作为合法的标签库,模块需要包含一个名为register的模块级变量。这个变量是template.Library的实例,是所有注册标签和过滤器的数据结构。
from django import template register = template.Library()
1).自定义模板过滤器
自定义过滤器就是有一个或两个参数的Python函数。过滤器函数应该总有返回值,而且不能触发异常,它们都应该静静的失败。 如果有一个错误发生,它们要么返回原始的输入字符串,要么返回空的字符串,无论哪个都可以。例如:
def cut(value, arg): "Removes all values of arg from the given string" return value.replace(arg, ‘‘)
当你在定义你的过滤器时,你需要用 Library 实例来注册它,这样就能通过Django的模板语言来使用了:
register.filter(‘cut‘, cut)
如果你使用的是Python 2.4或更新,你可以使用 register.filter() 作为一个装饰器:
from django import template register = template.Library() @register.filter(name=‘cut‘) def cut(value, arg): return value.replace(arg, ‘‘)
2).自定义模板标签
模板系统的两步处理过程:编译和呈现。当Django编译一个模板时,它将原始模板分成一个个 节点 。每个节点都是 django.template.Node 的一个实例,并且具备 render() 方法。当你调用一个已编译模板的 render() 方法时,模板就会用给定的context来调用每个在它的节点列表上的节点的 render() 方法。
(a).编写编译函数:当遇到一个模板标签(template
tag)时,模板解析器就会把标签包含的内容,以及模板解析器自己作为参数调用一个python函数。
这个函数负责返回一个和当前模板标签内容相对应的节点(Node)的实例。例如,写一个显示当前日期的模板标签:
<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>
这个函数的分析器会获取参数并创建一个 Node 对象:
from django import template register = template.Library() def do_current_time(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, format_string = token.split_contents() except ValueError: msg = ‘%r tag requires a single argument‘ % token.split_contents()[0] raise template.TemplateSyntaxError(msg) return CurrentTimeNode(format_string[1:-1])
注:token.contents 是包含有标签原始内容的字符串。模板标签编译函数 必须 返回一个 Node 子类,返回其它值都是错的。
(b).编写模板节点:编写自定义标签的第二步就是定义一个拥有 render() 方法的 Node 子类。
import datetime class CurrentTimeNode(template.Node): def __init__(self, format_string): self.format_string = str(format_string) def render(self, context): now = datetime.datetime.now() return now.strftime(self.format_string)
这两个函数( __init__ 和 render )与模板处理中的两步(编译与渲染)直接对应。 这样,初始化函数仅仅需要存储后面要用到的格式字符串,而
render() 函数才做真正的工作。
(c).注册标签
register.tag(‘current_time‘, do_current_time)
和注册过滤器类似,也可以在Python2.4及其以上版本中使用 register.tag 修饰:
@register.tag(name="current_time") def do_current_time(parser, token): # ...
4.简单标签
许多模板标签接收单一的字符串参数或者一个模板变量 引用,然后独立地根据输入变量和一些其它外部信息进行处理并返回一个字符串.为了简化这类标签,Django提供了一个帮助函数simple_tag。这个函数是django.template.Library的一个方法,它接受一个只有一个参数的函数作参数。
我们之前的的 current_time 函数于是可以写成这样:
def current_time(format_string): try: return datetime.datetime.now().strftime(str(format_string)) except UnicodeEncodeError: return ‘‘ register.simple_tag(current_time)
在Python 2.4中,也可以使用修饰语法:
@register.simple_tag def current_time(string): # ...
有关 simple_tag 辅助函数,需要注意下面一些事情:
- 传递给我们的函数的只有(单个)参数。
- 在我们的函数被调用的时候,检查必需参数个数的工作已经完成了,所以我们不需要再做这个工作。
- 参数两边的引号(如果有的话)已经被截掉了,所以我们会接收到一个普通字符串。
5.包含标签
另外一类常用的模板标签是通过渲染 其他 模板显示数据的。意思就是说在一个模板中通过调用另一个模板显示数据。
首先,我们定义一个函数,通过给定的参数生成一个字典形式的结果。 需要注意的是,我们只需要返回字典类型的结果就行了,它将被用做模板片断的context。
from books.models import Book @register.inclusion_tag(‘book_snippet.html‘) def books_for_author(author): books = Book.objects.filter(authors__id=author.id) return {‘books‘: books}
接下来,我们创建用于渲染标签输出的模板book_snippet.html。
<ul> {% for book in books %} <li>{{ book.title }}</li> {% endfor %} </ul>
最后,创建主模板result_snippet.html并调用。
# view.py from django.http import HttpResponse from django.template import loader, RequestContext from books.models import Author def view_2(request): # ... author = Author.objects.get(first_name="wang",last_name="yongbin") t = loader.get_template(‘result_snippet.html‘) c = RequestContext(request, {‘author‘: author}, processors=[custom_proc]) return HttpResponse(t.render(c)) # url.py urlpatterns = patterns(‘‘, (r‘^inclusion/$‘,view_2), ) # result_snippet.html {% load mytag %} {% books_for_author author %}
最后显示作者为wang yongbin的书籍的列表。