ITEEDU

| 上一章 | 目 录 | 下一章 |

第九章 模板高级进阶

虽然和Django的模板语言的大多数交互都是模板作者的工作,但你可能想定制和扩展模板引擎,让它做一些它不能做的事情,或者是以其他方式让你的工作更轻松。

本章深入钻研Django的模板系统。 如果你想扩展模板系统或者只是对它的工作原理感觉到好奇,本章涉及了你需要了解的东西。 它也包含一个自动转意特征,当你继续使用django的时候随着时间推移你一定会注意这个安全考虑。

如果你想把Django的模版系统作为另外一个应用程序的一部分(比如,仅使用django的模板系统而不使用Django框架的其他部分),那你一定要读一下“配置独立模式下的模版系统”这一节。

模板语言回顾

首先,让我们快速回顾一下第四章介绍的若干专业术语

模板 是一个纯文本文件,或是一个用Django模板语言标记过的普通的Python字符串,一个模板可以包含区块标签和变量。 模板可以包含模板标签和变量。

区块标签 是在一个模板里面起作用的的标记,这个定义故意说的很含糊,比如,一个 区块标签可以生成内容,可以作为一个控制结构( if 语句或 for 循环), 可以获取数据库内容,或者访问其他的模板标签。 这个定义故意搞得模糊不清。 例如,一个模版标签能够产生作为控制结构的内容(一个 iffor 循环),

区块标签被 {%%} 包含:

{% 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 }}.

context 是一个传递给模板的名称到值的映射(类似Python字典)。

模板 渲染 就是是通过从context获取值来替换模板中变量并执行所有的区块标签。

关于这些基本概念更详细的内容,请参考第四章。

本章的其余部分讨论了扩展模板引擎的方法。 首先,我们快速的看一下第四章遗留的内容。

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对象然后渲染模板。 是为了能够清晰的说明所有步骤。

每个视图都给模板传入了三个相同的变量: appuserip_address 。如果我们能把这些冗余去掉会不会看起来更好?

创建 RequestContextcontext处理器 就是为了解决这个问题。 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处理器函数的list或者tuple。 在这里,我们传递了我们之前定义的函数 curstom_proc

  • 每个视图的context结构里不再包含 appuserip_address 等变量,因为这些由 custom_proc 函数提供了。

  • 每个视图 仍然 具有很大的灵活性,可以引入我们需要的任何模板变量。 在这个例子中, message 模板变量在每个视图中都不一样。

在第四章,我们介绍了 render_to_response() 这个快捷方式,它可以省掉调用 loader.get_template() ,然后创建一个 Context 对象,最后再调用模板对象的 render() 方法。 为了讲解context处理器底层是如何工作的,在上面的例子中我们没有使用 render_to_response() 。但是建议选择 render_to_response() 作为context的处理器。

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 ,所以使