The Django Book

Chapter 10: Extending the Template Engine

第十章: 深入模板引擎

Although most of your interactions with Djangos template language will be in the role of template author, you may want to customize and extend the template engine either to make it do something it doesnt already do, or to make your job easier in some other way.

虽然大多数Django模板语言之间的作用和模板作者需要的作用一样,但您仍可以定制并扩展模板引擎,让它做一些原本不能做到的事,或用同样的方式更容易地完成您的工作。

This chapter delves deep into the guts of Djangos template system. It covers what you need to know if you plan to extend the system or if youre just curious about how it works.

本章深入钻研Django的模板系统。如果你想扩展模板系统或者只是对它的工作原理感觉到好奇,本章涉及了你需要了解的东西.

If youre looking to use the Django template system as part of another application (i.e., without the rest of the framework), make sure to read the Configuring the Template System in Standalone Mode section later in the chapter.

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

Template Language Review

模板语言回顾

First, lets quickly review a number of terms introduced in Chapter 4:

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

A template is a text document, or a normal Python string, that is marked up using the Django template language. A template can contain block tags and variables.

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

A block tag is a symbol within a template that does something. This definition is deliberately vague. For example, a block tag can produce content, serve as a control structure (an if statement or for loop), grab content from a database, or enable access to other template tags.

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

Block tags are surrounded by {% and %} :

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

{% if is_logged_in %}
  Thanks for logging in!
{% else %}
  Please log in.
{% endif %}

A variable is a symbol within a template that outputs a value.

变量 是一个在模板里用来输出值的标记。

Variable tags are surrounded by {{ and }} :

变量标签被 {{}} 包含:

My first name is {{ first_name }}. My last name is {{ last_name }}.

A context is a name -> value mapping (similar to a Python dictionary) that is passed to a template.

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

A template renders a context by replacing the variable holes with values from the context and executing all block tags.

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

For more details about the basics of these terms, refer back to Chapter 4.

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

The rest of this chapter discusses ways of extending the template engine. First, though, lets take a quick look at a few internals left out of Chapter 4 for simplicity.

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

RequestContext and Context Processors

RequestContext和Context处理器

When rendering a template, you need a context. Usually this is an instance of django.template.Context , but Django also comes with a special subclass, django.template.RequestContext , that acts slightly differently. RequestContext adds a bunch of variables to your template context by default things like the HttpRequest object or information about the currently logged-in user.

你需要一段context来解析模板。一般情况下,这是一个 django.template.Context 的实例,不过在Django中还可以用一个特殊的子类, django.template.RequestContext ,这个运用起来稍微有些不同。 RequestContext 默认地在模板context中加入了一些变量,如 HttpRequest 对象或当前登录用户的相关信息。

Use RequestContext when you dont want to have to specify the same set of variables in a series of templates. For example, consider these four views:

当你不想在一系例模板中都明确指定一些相同的变量时,你应该使用 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)

def view_3(request):
# ...
    t = loader.get_template('template3.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the third view.'
    })
    return t.render(c)

def view_4(request):
    # ...
    t = loader.get_template('template4.html')
    c = Context({
        'app': 'My app',
        'user': request.user,
        'ip_address': request.META['REMOTE_ADDR'],
        'message': 'I am the fourth view.'
    })
    return t.render(c)

(Note that were deliberately not using the render_to_response() shortcut in these examples were manually loading the templates, constructing the context objects and rendering the templates. Were spelling out all of the steps for the purpose of clarity.)

(注意,在这些例子中,我们故意 使用 render_to_response() 这个快捷方法,而选择手动载入模板,手动构造context对象然后渲染模板。是为了能够清晰的说明所有步骤。)

Each view passes the same three variables app , user and ip_address to its template. Wouldnt it be nice if we could remove that redundancy?

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

RequestContext and context processors were created to solve this problem. Context processors let you specify a number of variables that get set in each context automatically without you having to specify the variables in each render_to_response() call. The catch is that you have to use RequestContext instead of Context when you render a template.

创建 RequestContextcontext处理器 就是为了解决这个问题。Context处理器允许你设置一些变量,它们会在每个context中自动被设置好,而不必每次调用 render_to_response() 时都指定。要点就是,当你渲染模板时,你要用 RequestContext 而不是 Context

The most low-level way of using context processors is to create some processors and pass them to RequestContext . Heres how the above example could be written with context processors:

最直接的做法是用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)

def view_3(request):
    # ...
    t = loader.get_template('template3.html')
    c = RequestContext(request, {'message': 'I am the third view.'},
            processors=[custom_proc])
    return t.render(c)

def view_4(request):
    # ...
    t = loader.get_template('template4.html')
    c = RequestContext(request, {'message': 'I am the fourth view.'},
            processors=[custom_proc])
    return t.render(c)

Lets step through this code:

我们来通读一下代码:

  • First, we define a function custom_proc . This is a context processor it takes an HttpRequest object and returns a dictionary of variables to use in the template context. Thats all it does.

  • 首先,我们定义一个函数 custom_proc 。这是一个context处理器,它接收一个 HttpRequest 对象,然后返回一个字典,这个字典中包含了可以在模板context中使用的变量。它就做了这么多。

  • Weve changed the four view functions to use RequestContext instead of Context . There are two differences in how the context is constructed. One, RequestContext requires the first argument to be an HttpRequest object the one that was passed into the view function in the first place (request ). Two, RequestContext takes an optional processors argument, which is a list or tuple of context processor functions to use. Here, we pass in custom_proc , the custom processor we defined above.

  • 我们在这四个视图函数中用 RequestContext 代替了 Context 。在context对象的构建上有两个不同点。一, RequestContext 的第一个参数需要传递一个 HttpRequest 对象,就是传递给视图函数的第一个参数( request )。二, RequestContext 有一个可选的参数 processors ,这是一个包含context处理器函数的list或者tuple。在这里,我们传递了我们之前定义的函数 curstom_proc

  • Each view no longer has to include app , user or ip_address in its context construction, because those are provided by custom_proc .

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

  • Each view still has the flexibility to introduce any custom template variables it might need. In this example, the message template variable is set differently in each view.

O5akXl <a href=”http://xrdylvofgyks.com/“>xrdylvofgyks</a>, [url=http://jpknyzrymcvm.com/]jpknyzrymcvm[/url], [link=http://kanomsrvtkmd.com/]kanomsrvtkmd[/link], http://avajxtraqqjg.com/

In Chapter 4, we introduced the render_to_response() shortcut, which saves you from having to call loader.get_template() , then create a Context , then call the render() method on the template. In order to demonstrate the lower-level workings of context processors, the above examples didnt use render_to_response() , . But its possible and preferable to use context processors with render_to_response() . Do this with the context_instance argument, like so:

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

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]))

def view_3(request):
    # ...
    return render_to_response('template3.html',
        {'message': 'I am the third view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))

def view_4(request):
    # ...
    return render_to_response('template4.html',
        {'message': 'I am the fourth view.'},
        context_instance=RequestContext(request, processors=[custom_proc]))

Here, weve trimmed down each views template rendering code to a single (wrapped) line.

在这,我们将每个视图的模板渲染代码写成了一个单行。

This is an improvement, but, evaluating the conciseness of this code, we have to admit were now almost overdosing on the other end of the spectrum. Weve removed redundancy in data (our template variables) at the cost of adding redundancy in code (in the processors call). Using context processors doesnt save you much typing if you have to type processors all the time.

虽然这是一种改进,但是,请考虑一下这段代码的简洁性,我们现在不得不承认的是在 另外 一方面有些过分了。我们以代码冗余(在 processors 调用中)的代价消除了数据上的冗余(我们的模板变量)。由于你不得不一直键入 processors ,所以使用context处理器并没有减少太多的打字次数。

For that reason, Django provides support for global context processors. The TEMPLATE_CONTEXT_PROCESSORS setting designates which context processors should always be applied to RequestContext . This removes the need to specify processors each time you use RequestContext .

Django因此提供对 全局 context处理器的支持。 TEMPLATE_CONTEXT_PROCESSORS 指定了 总是 使用哪些 context processors 。这样就省去了每次使用 RequestContext 都指定 processors 的麻烦^_^。

By default, TEMPLATE_CONTEXT_PROCESSORS is set to the following:

ilR94N <a href=”http://bxcyktdovtcx.com/“>bxcyktdovtcx</a>, [url=http://svkmchcyqtpi.com/]svkmchcyqtpi[/url], [link=http://orlgauoznlpg.com/]orlgauoznlpg[/link], http://hcmltazhxlgv.com/

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.core.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
)

This setting is a tuple of callables that use the same interface as our custom_proc function above functions that take a request object as their argument and return a dictionary of items to be merged into the context. Note that the values in TEMPLATE_CONTEXT_PROCESSORS are specified as strings , which means the processors are required to be somewhere on your Python path (so you can refer to them from the setting).

这个设置是一个可调用函数的Tuple,其中的每个函数使用了和上文中我们的 custom_proc 相同的接口:接收一个request对象作为参数,返回一个包含了将被合并到context中的项的字典。请注意 TEMPLATE_CONTEXT_PROCESSORS 中的值是以 strings 的形式给出的,这意味着这些处理器必须在你的python路径中的某处(这样你才能在设置中引用它们)

Each processor is applied in order. That is, if one processor adds a variable to the context and a second processor adds a variable with the same name, the second will override the first.

RkbRhS <a href=”http://ssvglzgqktrs.com/“>ssvglzgqktrs</a>, [url=http://tzwexwjpftqx.com/]tzwexwjpftqx[/url], [link=http://siituwbkjtet.com/]siituwbkjtet[/link], http://nyplxetpyqkq.com/

Django provides a number of simple context processors, including the ones that are enabled by default:

Django提供了几个简单的context处理器,有些在默认情况下被启用的。

django.core.context_processors.auth

django.core.context_processors.auth

If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain these variables:

cZgBaF <a href=”http://wqijjqhtuafe.com/“>wqijjqhtuafe</a>, [url=http://ymyewzllgpwg.com/]ymyewzllgpwg[/url], [link=http://ynrosouewqcv.com/]ynrosouewqcv[/link], http://taptrifhfbme.com/

  • user : A django.contrib.auth.models.User instance representing the current logged-in user (or an AnonymousUser instance, if the client isnt logged in).

  • user :一个 django.contrib.auth.models.User 实例,描述了当前登录用户(或者一个 AnonymousUser 实例,如果客户端没有登录)。

  • messages : A list of messages (as strings) for the current logged-in user. Behind the scenes, this variable calls request.user.get_and_delete_messages() for every request. That method collects the users messages and deletes them from the database.

  • messages :一个当前登录用户的消息列表(字符串)。在后台,对每一个请求这个变量都调用 request.user.get_and_delete_messages() 方法。这个方法收集用户的消息然后把它们从数据库中删除。

  • perms : An instance of django.core.context_processors.PermWrapper , which represents the permissions the current logged-in user has.

  • permsdjango.core.context_processors.PermWrapper 的一个实例,包含了当前登录用户有哪些权限。

See Chapter 12 for more information on users, permissions, and messages.

关于users、permissions和messages的更多内容请参考第12章。

django.core.context_processors.debug

django.core.context_processors.debug

This processor pushes debugging information down to the template layer. If TEMPLATE_CONTEXT_PROCESSORS contains this processor, every RequestContext will contain these variables:

ae2cpK <a href=”http://zekedeavaywg.com/“>zekedeavaywg</a>, [url=http://kqzsnzwpeoko.com/]kqzsnzwpeoko[/url], [link=http://ncoswfhxxpgm.com/]ncoswfhxxpgm[/link], http://vrswcfxjqbnl.com/

  • debug : The value of your DEBUG setting (either True or False ). You can use this variable in templates to test whether youre in debug mode.

  • debug :你设置的 DEBUG 的值( TrueFalse )。你可以在模板里面用这个变量测试是否处在debug模式下。

  • sql_queries : A list of {'sql': ..., 'time': ...} dictionaries representing every SQL query that has happened so far during the request and how long it took. The list is in the order in which the queries were issued.

  • sql_queries :包含类似于 {'sql': ..., 'time': ...} 的字典的一个列表, 记录了这个请求期间的每个SQL查询以及查询所耗费的时间。这个列表是按照请求顺序进行排列的。

Because debugging information is sensitive, this context processor will only add variables to the context if both of the following conditions are true:

由于调试信息比较敏感,所以这个context处理器只有当同时满足下面两个条件的时候才有效:

  • The DEBUG setting is True .

VB156E <a href=”http://xsstdaarnodo.com/“>xsstdaarnodo</a>, [url=http://sjvwzwjuntje.com/]sjvwzwjuntje[/url], [link=http://rllxfurtgmdb.com/]rllxfurtgmdb[/link], http://liaaqrivlsgl.com/

  • The request came from an IP address in the INTERNAL_IPS setting.

  • 请求的ip应该包含在 INTERNAL_IPS 的设置里面。

django.core.context_processors.i18n

django.core.context_processors.i18n

If this processor is enabled, every RequestContext will contain these variables:

如果这个处理器启用,每个 RequestContext 将包含下面的变量:

  • LANGUAGES : The value of the LANGUAGES setting.

  • LANGUAGESLANGUAGES 选项的值。

  • LANGUAGE_CODE : request.LANGUAGE_CODE if it exists; otherwise, the value of the LANGUAGE_CODE setting.

  • LANGUAGE_CODE :如果 request.LANGUAGE_CODE 存在,就等于它;否则,等同于 LANGUAGE_CODE 设置。

Appendix E provides more information about these two settings.

附录E提供了有关这两个设置的更多的信息。

django.core.context_processors.request

django.core.context_processors.request

If this processor is enabled, every RequestContext will contain a variable request , which is the current HttpRequest object. Note that this processor is not enabled by default; you have to activate it.

zEZko4 <a href=”http://davktzvegpga.com/“>davktzvegpga</a>, [url=http://dvvitkvkrqhj.com/]dvvitkvkrqhj[/url], [link=http://zmxecatgkrei.com/]zmxecatgkrei[/link], http://qxqjkagkmvbm.com/

Guidelines for Writing Your Own Context Processors

写Context处理器的一些建议

Here are a few tips for rolling your own:

编写处理器的一些建议:

  • Make each context processor responsible for the smallest subset of functionality possible. Its easy to use multiple processors, so you might as well split functionality into logical pieces for future reuse.

  • 使每个context处理器完成尽可能小的功能。 使用多个处理器是很容易的,所以你可以根据逻辑块来分解功能以便将来重用。

  • Keep in mind that any context processor in TEMPLATE_CONTEXT_PROCESSORS will be available in every template powered by that settings file, so try to pick variable names that are unlikely to conflict with variable names your templates might be using independently. As variable names are case-sensitive, its not a bad idea to use all caps for variables a processor provides.

  • 要注意 TEMPLATE_CONTEXT_PROCESSORS 里的context processor 将会在 每个 模板中有效,所以要变量的命名不要和模板的变量冲突。 变量名是大小写敏感的,所以processor的变量全用大写是个不错的主意。

  • It doesnt matter where on the filesystem they live, as long as theyre on your Python path so you can point to them from the TEMPLATE_CONTEXT_PROCESSORS setting. With that said, the convention is to save them in a file called context_processors.py within your app or project.

  • 只要它们存放在你的Python的搜索路径中,它们放在哪个物理路径并不重要,这样你可以在 TEMPLATE_CONTEXT_PROCESSORS 设置里指向它们。 也就是说,你要把它们放在app或者project目录里名为 context_processors.py 的文件。

Inside Template Loading

模板加载的内幕

Generally, youll store templates in files on your filesystem, but you can use custom template loaders to load templates from other sources.

一般说来,你会把模板以文件的方式存储在文件系统中,但是你也可以使用自定义的 template loaders 从其他来源加载模板。

Django has two ways to load templates:

Django有两种方法加载模板

  • django.template.loader.get_template(template_name) : get_template returns the compiled template (a Template object) for the template with the given name. If the template doesnt exist, a TemplateDoesNotExist exception will be raised.

  • django.template.loader.get_template(template_name)get_template 根据给定的模板名称返回一个已编译的模板(一个 Template 对象)。如果模板不存在,就触发 TemplateDoesNotExist 的异常。

  • django.template.loader.select_template(template_name_list) : select_template is just like get_template , except it takes a list of template names. Of the list, it returns the first template that exists. If none of the templates exist, a TemplateDoesNotExist exception will be raised.

  • django.template.loader.select_template(template_name_list)select_template 很像 get_template ,不过它是以模板名称的列表作为参数的,并且它返回第一个存在的模板。如果模板都不存在,将会触发 TemplateDoesNotExist 异常。

As covered in Chapter 4, each of these functions by default uses your TEMPLATE_DIRS setting to load templates. Internally, however, these functions actually delegate to a template loader for the heavy lifting.

正如在第四章中所提到的,默认情况下这些函数使用 TEMPLATE_DIRS 的设置来载入模板。但是,在内部这些函数可以指定一个模板加载器来完成这些繁重的任务。

Some of loaders are disabled by default, but you can activate them by editing the TEMPLATE_LOADERS setting. TEMPLATE_LOADERS should be a tuple of strings, where each string represents a template loader. These template loaders ship with Django:

一些加载器默认被禁用,但是你可以通过编辑 TEMPLATE_LOADERS 设置来激活它们。 TEMPLATE_LOADERS 应当是一个字符串的元组,其中每个字符串都表示一个模板加载器。这些模板加载器随Django一起发布。

django.template.loaders.filesystem.load_template_source : This loader loads templates from the filesystem, according to TEMPLATE_DIRS . It is enabled by default.

django.template.loaders.filesystem.load_template_source : 这个加载器根据 TEMPLATE_DIRS 的设置从文件系统加载模板。在默认情况下这个加载器被启用.

django.template.loaders.app_directories.load_template_source : This loader loads templates from Django applications on the filesystem. For each application in INSTALLED_APPS , the loader looks for a templates subdirectory. If the directory exists, Django looks for templates there.

django.template.loaders.app_directories.load_template_source : 这个加 载器从文件系统上的Django应用中加载模板。对 INSTALLED_APPS 中的每个应用,这个加 载器会查找一个 templates 子目录。如果这个目录存在,Django就在那里寻找模板。

This means you can store templates with your individual applications, making it easy to distribute Django applications with default templates. For example, if INSTALLED_APPS contains ('myproject.polls', 'myproject.music') , then get_template('foo.html') will look for templates in this order:

这意味着你可以把模板和你的应用一起保存,从而使得Django应用更容易和默认模板一起发布。例如,如果 INSTALLED_APPS 包含 ('myproject.polls','myproject.music') ,那么 get_template('foo.html') 会按这个顺序查找模板:

  • /path/to/myproject/polls/templates/foo.html

  • /path/to/myproject/polls/templates/foo.html

  • /path/to/myproject/music/templates/foo.html

zS7gE0 <a href=”http://xihwijxziqzw.com/“>xihwijxziqzw</a>, [url=http://latxmxlseqcl.com/]latxmxlseqcl[/url], [link=http://fwlitcrfihxf.com/]fwlitcrfihxf[/link], http://yxtsdtenuwcv.com/

Note that the loader performs an optimization when it is first imported: it caches a list of which INSTALLED_APPS packages have a templates subdirectory.

请注意加载器在首次被导入的时候会执行一个优化:它会缓存一个列表,这个列表包含了 INSTALLED_APPS 中带有 templates 子目录的包。

This loader is enabled by default.

这个加载器默认启用。

django.template.loaders.eggs.load_template_source : This loader is just like app_directories , except it loads templates from Python eggs rather than from the filesystem. This loader is disabled by default; youll need to enable it if youre using eggs to distribute your application.

django.template.loaders.eggs.load_template_source : 这个加载器类似 app_directories ,只不过它从Python eggs而不是文件系统中加载模板。这个加载器默认被禁用;如果你使用eggs来发布你的应用,那么你就需要启用它。

Django uses the template loaders in order according to the TEMPLATE_LOADERS setting. It uses each loader until a loader finds a match.

Django按照 TEMPLATE_LOADERS 设置中的顺序使用模板加载器。它逐个使用每个加载器直至找到一个匹配的模板。

Extending the Template System

扩展模板系统

Now that you understand a bit more about the internals of the template system, lets look at how to extend the system with custom code.

既然你已经对模板系统的内幕了解多了一些,让我们来看看如何使用自定义的代码来拓展这个系统吧。

Most template customization comes in the form of custom template tags and/or filters. Although the Django template language comes with many built-in tags and filters, youll probably assemble your own libraries of tags and filters that fit your own needs. Fortunately, its quite easy to define your own functionality.

绝大部分的模板定制是以自定义标签/过滤器的方式来完成的。尽管Django模板语言自带了许多内建标签和过滤器,但是你可能还是需要组建你自己的标签和过滤器库来满足你的需要。幸运的是,定义你自己的功能非常容易。

Creating a Template Library

创建一个模板库

Whether youre writing custom tags or filters, the first thing to do is to create a template library a small bit of infrastructure Django can hook into.

不管是写自定义标签还是过滤器,第一件要做的事是给 template library 创建使Django能够勾入的机制。

Creating a template library is a two-step process:

创建一个模板库分两步走:

First, decide which Django application should house the template library. If youve created an app via manage.py startapp , you can put it in there, or you can create another app solely for the template library.

第一,决定哪个Django应用应当拥有这个模板库。如果你通过 manage.py startapp 创建了一个应用,你可以把它放在那里,或者你可以为模板库单独创建一个应用。

Whichever route you take, make sure to add the app to your INSTALLED_APPS setting. Well explain this shortly.

无论你采用何种方式,请确保把你的应用添加到 INSTALLED_APPS 中。我们稍后会解释这一点。

Second, create a templatetags directory in the appropriate Django applications package. It should be on the same level as models.py , views.py , and so forth. For example:

第二,在适当的Django应用包里创建一个 templatetags 目录。这个目录应当和 models.pyviews.py 等处于同一层次。例如:

books/
    __init__.py
    models.py
    templatetags/
    views.py

Create two empty files in the templatetags directory: an __init__.py file (to indicate to Python that this is a package containing Python code) and a file that will contain your custom tag/filter definitions. The name of the latter file is what youll use to load the tags later. For example, if your custom tags/filters are in a file called poll_extras.py , youd write the following in a template:

templatetags 中创建两个空文件:一个 __init__.py (告诉Python这是 一个包含了Python代码的包)和一个用来存放你自定义的标签/过滤器定义的文件。第二个文件 的名字稍后将用来加载标签。例如,如果你的自定义标签/过滤器在一个叫作 poll_extras.py 的文件中,你需要在模板中写入如下内容:

{% load poll_extras %}

The {% load %} tag looks at your INSTALLED_APPS setting and only allows the loading of template libraries within installed Django applications. This is a security feature; it allows you to host Python code for many template libraries on a single computer without enabling access to all of them for every Django installation.

{% load %} 标签检查 INSTALLED_APPS 中的设置,仅允许加载已安装的Django应用程序中的模板库。这是一个安全特性。它可以让你在一台电脑上部署很多的模板库的代码,而又不用把它们暴露给每一个Django安装。

If you write a template library that isnt tied to any particular models/views, its valid and quite normal to have a Django application package that contains only a templatetags package. Theres no limit on how many modules you put in the templatetags package. Just keep in mind that a {% load %} statement will load tags/filters for the given Python module name, not the name of the application.

如果你写了一个不和任何模型/视图关联的模板库,那么得到一个仅包含 templatetags 包的Django应用程序包是完全正常的。对于在 templatetags 包中放置多少个模块没有做任何的限制。需要了解的是: {% load %} 语句会为指定的Python模块名(而非应用程序名)加载标签或过滤器。

Once youve created that Python module, youll just have to write a bit of Python code, depending on whether youre writing filters or tags.

一旦创建了Python模块,你只需根据是要编写过滤器还是标签来相应的编写一些Python代码。

To be a valid tag library, the module must contain a module-level variable named register that is a template.Library instance. This template.Library instance is the data structure in which all the tags and filters are registered. So, near the top of your module, insert the following:

要成为有效的标签库,模块必须包含一个模块级的变量: register ,这是一个 template.Library 的实例。这个 template.Library 实例是包含所有已注册的标签及过滤器的数据结构。因此,在模块的顶部位置插入下述代码:

from django import template

register = template.Library()

Note

备注

For a good number of examples, read the source code for Djangos default filters and tags. Theyre in django/template/defaultfilters.py and django/template/defaulttags.py , respectively. Some applications in django.contrib also contain template libraries.

请阅读Django默认的过滤器和标签的源码,那里有大量的例子。他们分别为: django/template/defaultfilters.pydjango/template/defaulttags.py 。某些应用程序在 django.contrib 中也包含模板库。

Once youve created this register variable, youll use it to create template filters and tags.

创建 register 变量后,你就可以使用它来创建模板的过滤器和标签了。

Writing Custom Template Filters

自定义模板过滤器

Custom filters are just Python functions that take one or two arguments:

自定义过滤器就是有一个或两个参数的Python函数:

  • The value of the variable (input)

  • (输入)变量的值

  • The value of the argument, which can have a default value or be left out altogether

  • 参数的值, 可以是默认值或者完全留空

For example, in the filter {{ var|foo:"bar" }} , the filter foo would be passed the contents of the variable var and the argument "bar" .

例如,在过滤器 {{ var|foo:"bar" }} 中 ,过滤器 foo 会被传入变量 var 和参数 bar 的内容。

Filter functions should always return something. They shouldnt raise exceptions, and they should fail silently. If theres an error, they should return either the original input or an empty string, whichever makes more sense.

过滤器函数应该总有返回值,而且不能触发异常,它们都应该静静的失败。如果有一个错误发生,它们要么返回原始的输入字符串,要么返回空的字符串,无论哪个都可以。

Heres an example filter definition:

这里是一些定义过滤器的例子:

def cut(value, arg):
    "Removes all values of arg from the given string"
    return value.replace(arg, '')

And heres an example of how that filter would be used:

这里是一些如何使用过滤器的例子:

{{ somevariable|cut:"0" }}

Most filters dont take arguments. In this case, just leave the argument out of your function:

大多数过滤器并不需要参数。下面的例子把参数从你的函数中拿掉了:

def lower(value): # Only one argument.
    "Converts a string into all lowercase"
    return value.lower()

When youve written your filter definition, you need to register it with your Library instance, to make it available to Djangos template language:

当你在定义你的过滤器时,你需要用 Library 实例来注册它,这样就能通过Django的模板语言来使用了:

register.filter('cut', cut)
register.filter('lower', lower)

The Library.filter() method takes two arguments:

Library.filter() 方法需要两个参数:

  • The name of the filter (a string)

  • 过滤器的名称(一个字串)

  • The filter function itself

  • 过滤器函数本身

If youre using Python 2.4 or above, you can use register.filter() as a decorator instead:

如果你使用的是Python 2.4或更新,你可以使用 register.filter() 作为一个装饰器:

@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')

@register.filter
def lower(value):
    return value.lower()

If you leave off the name argument, as in the second example, Django will use the functions name as the filter name.

像第二个例子中,如果你不使用 name 参数,那么Django将会使用函数名作为过滤器的名字。

Here, then, is a complete template library example, supplying the cut filter:

下面是一个完整的模板库的例子,提供了一个 cut 过滤器:

from django import template

register = template.Library()

@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')

Writing Custom Template Tags

自定义模板标签

Tags are more complex than filters, because tags can do nearly anything.

标签要比过滤器复杂些,标签几乎能做任何事情。

Chapter 4 describes how the template system works in a two-step process: compiling and rendering. To define a custom template tag, you need to tell Django how to manage both steps when it gets to your tag.

第四章描述了模板系统的两步处理过程:编译和呈现。为了自定义一个这样的模板标签,你需要告诉Django当遇到你的标签时怎样进行这过程。

When Django compiles a template, it splits the raw template text into nodes . Each node is an instance of django.template.Node and has a render() method. Thus, a compiled template is simply a list of Node objects.

当Django编译一个模板时,它将原始模板分成一个个 节点 。每个节点都是 django.template.Node 的一个实例,并且具备 render() 方法。于是,一个已编译的模板就是 Node 对象的一个列表。

When you call render() on a compiled template, the template calls render() on each Node in its node list, with the given context. The results are all concatenated together to form the output of the template. Thus, to define a custom template tag, you specify how the raw template tag is converted into a Node (the compilation function) and what the nodes render() method does.

当你调用一个已编译模板的 render() 方法时,模板就会用给定的context来调用每个在它的节点列表上的节点的 render() 方法。所以,为了定义一个自定义的模板标签,你需要明确这个模板标签转换为一个 Node (已编译的函数)和这个node的 render() 方法。

In the sections that follow, we cover all the steps in writing a custom tag.

在下面的章节中,我们将详细解说写一个自定义标签时的所有步骤。

Writing the Compilation Function
编写编译函数

For each template tag it encounters, the template parser calls a Python function with the tag contents and the parser object itself. This function is responsible for returning a Node instance based on the contents of the tag.

当遇到一个模板标签(template tag)时,模板解析器就会把标签包含的内容,以及模板解析器自己作为参数调用一个python函数。这个函数负责返回一个和当前模板标签内容相对应的节点(Node)的实例。

For example, lets write a template tag, {% current_time %} , that displays the current date/time, formatted according to a parameter given in the tag, in strftime syntax (see http://www.djangoproject.com/r/python/strftime/ ). Its a good idea to decide the tag syntax before anything else. In our case, lets say the tag should be used like this:

例如,写一个显示当前日期的模板标签:{% current_time %},该标签会根据参数指定的 strftime 格式(参见: http://www.djangoproject.com/r/python/strftime/ )显示当前时间。在继续做其它事情以前,先决定标签的语法是一个好主意。在我们的例子里,该标签将会像这样被使用:

<p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

Note

备注

Yes, this template tag is redundantDjangos default {% now %} tag does the same task with simpler syntax. This template tag is presented here just for example purposes.

没错, 这个模板标签是多余的,Django默认的 {% now %} 用更简单的语法完成了同样的工作。这个模板标签在这里只是作为一个例子。

The parser for this function should grab the parameter and create a Node object:

这个函数的分析器会获取参数并创建一个 Node 对象:

from django import template

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.contents[0]
        raise template.TemplateSyntaxError(msg)
    return CurrentTimeNode(format_string[1:-1])

Theres actually a lot going here:

其实这儿包含了不少东西:

  • parser is the template parser object. We dont need it in this example.

  • parser 是模板分析器对象,在这个例子中我们没有使用它。

  • token.contents is a string of the raw contents of the tag. In our example, its 'current_time "%Y-%m-%d %I:%M %p"' .

  • token.contents 是包含有标签原始内容的字符串。在我们的例子中,它是 'current_time "%Y-%m-%d %I:%M %p"'

  • The token.split_contents() method separates the arguments on spaces while keeping quoted strings together. Avoid using token.contents.split() (which just uses Pythons standard string-splitting semantics). Its not as robust, as it naively splits on all spaces, including those within quoted strings.

  • token.split_contents() 方法按空格拆分参数同时保证引号中的字符串在一起。应该避免使用 token.contents.split() (仅是使用Python的标准字符串拆分),它不够健壮,因为它只是简单的按照 所有 空格进行拆分,包括那些引号引起来的字符串中的空格。

  • This function is responsible for raising django.template.TemplateSyntaxError , with helpful messages, for any syntax error.

  • 这个函数负责抛出 django.template.TemplateSyntaxError ,同时提供所有语法错误的有用信息。

  • Dont hard-code the tags name in your error messages, because that couples the tags name to your function. token.split_contents()[0] will always be the name of your tageven when the tag has no arguments.

  • 不要把标签名称硬编码在你的错误信息中,因为这样会把标签名称和你的函数耦合在一起。 token.split_contents()[0] 总会是 是你的标签的名称,即使标签没有参数。

  • The function returns a CurrentTimeNode (which well create shortly) containing everything the node needs to know about this tag. In this case, it just passes the argument "%Y-%m-%d %I:%M %p" . The leading and trailing quotes from the template tag are removed with format_string[1:-1] .

  • 这个函数返回一个 CurrentTimeNode (稍后我们将创建它),它包含了节点需要知道的关于这个标签的全部信息。在这个例子中,它只是传递了参数 "%Y-%m-%d %I:%M %p" 。模板标签开头和结尾的引号使用 format_string[1:-1] 除去。

  • Template tag compilation functions must return a Node subclass; any other return value is an error.

  • 模板标签编译函数 必须 返回一个 Node 子类,返回其它值都是错的。

Writing the Template Node
编写模板节点

The second step in writing custom tags is to define a Node subclass that has a render() method. Continuing the preceding example, we need to define CurrentTimeNode :

编写自定义标签的第二步就是定义一个拥有 render() 方法的 Node 子类。继续前面的例子,我们需要定义 CurrentTimeNode

import datetime

class CurrentTimeNode(template.Node):

    def __init__(self, format_string):
        self.format_string = format_string

    def render(self, context):
        now = datetime.datetime.now()
        return now.strftime(self.format_string)

These two functions (__init__ and render ) map directly to the two steps in template processing (compilation and rendering). Thus, the initialization function only needs to store the format string for later use, and the render() function does the real work.

这两个函数( __init__render )与模板处理中的两步(编译与渲染)直接对应。这样,初始化函数仅仅需要存储后面要用到的格式字符串,而 render() 函数才做真正的工作。

Like template filters, these rendering functions should fail silently instead of raising errors. The only time that template tags are allowed to raise errors is at compilation time.

与模板过滤器一样,这些渲染函数应该捕获错误,而不是抛出错误。模板标签只能在编译的时候才能抛出错误。

Registering the Tag
注册标签

Finally, you need to register the tag with your modules Library instance. Registering custom tags is very similar to registering custom filters (as explained above). Just instantiate a template.Library instance and call its tag() method. For example:

最后,你需要用你的模块 Library 实例注册这个标签。注册自定义标签与注册自定义过滤器非常类似(如前文所述)。实例化一个 template.Library 实例然后调用它的 tag() 方法。例如:

register.tag('current_time', do_current_time)

The tag() method takes two arguments:

tag() 方法需要两个参数:

The name of the template tag (string). If this is left out, the

模板标签的名字(字符串)。如果被遗漏的话,将会使用编译函数的名字。

name of the compilation function will be used.

The compilation function.

编译函数。

As with filter registration, it is also possible to use register.tag as a decorator in Python 2.4 and above:

和注册过滤器类似,也可以在Python2.4及其以上版本中使用 register.tag 修饰:

@register.tag(name="current_time")
def do_current_time(parser, token):
    # ...

@register.tag
def shout(parser, token):
    # ...

If you leave off the name argument, as in the second example, Django will use the functions name as the tag name.

如果你像在第二个例子中那样忽略 name 参数的话,Django会使用函数名称作为标签名称。

Setting a Variable in the Context
在上下文中设置变量

The previous sections example simply returned a value. Often its useful to set template variables instead of returning values. That way, template authors can just use the variables that your template tags set.

前一节的例子只是简单的返回一个值。很多时候设置一个模板变量而非返回值也很有用。那样,模板作者就只能使用你的模板标签所设置的变量。

To set a variable in the context, use dictionary assignment on the context object in the render() method. Heres an updated version of CurrentTimeNode that sets a template variable, current_time , instead of returning it:

要在上下文中设置变量,在 render() 函数的context对象上使用字典赋值。这里是一个修改过的 CurrentTimeNode ,其中设定了一个模板变量 current_time ,并没有返回它:

class CurrentTimeNode2(template.Node):

    def __init__(self, format_string):
        self.format_string = format_string

    def render(self, context):
        now = datetime.datetime.now()
        context['current_time'] = now.strftime(self.format_string)
        return ''

Note that render() returns an empty string. render() should always return a string, so if all the template tag does is set a variable, render() should return an empty string.

注意 render() 返回了一个空字符串。 render() 应当总是返回一个字符串,所以如果模板标签只是要设置变量, render() 就应该返回一个空字符串。

Heres how youd use this new version of the tag:

你应该这样使用这个新版本的标签:

{% current_time2 "%Y-%M-%d %I:%M %p" %}
<p>The time is {{ current_time }}.</p>

But theres a problem with CurrentTimeNode2 : the variable name current_time is hard-coded. This means youll need to make sure your template doesnt use {{ current_time }} anywhere else, because {% current_time2 %} will blindly overwrite that variables value.

但是 CurrentTimeNode2 有一个问题: 变量名 current_time 是硬编码的。这意味着你必须确定你的模板在其它任何地方都不使用 {{ current_time }} ,因为 {% current_time2 %} 会盲目的覆盖该变量的值。

A cleaner solution is to make the template tag specify the name of the variable to be set, like so:

一种更简洁的方案是由模板标签来指定需要设定的变量的名称,就像这样:

{% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
<p>The current time is {{ my_current_time }}.</p>

To do so, youll need to refactor both the compilation function and the Node class, as follows:

为此,你需要重构编译函数和 Node 类,如下所示:

import re

class CurrentTimeNode3(template.Node):

    def __init__(self, format_string, var_name):
        self.format_string = format_string
        self.var_name = var_name

    def render(self, context):
        now = datetime.datetime.now()
        context[self.var_name] = now.strftime(self.format_string)
        return ''

def do_current_time(parser, token):
    # This version uses a regular expression to parse tag contents.
    try:
        # Splitting by None == splitting by spaces.
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        msg = '%r tag requires arguments' % token.contents[0]
        raise template.TemplateSyntaxError(msg)

    m = re.search(r'(.*?) as (\w+)', arg)
    if m:
        fmt, var_name = m.groups()
    else:
        msg = '%r tag had invalid arguments' % tag_name
        raise template.TemplateSyntaxError(msg)

    if not (fmt[0] == fmt[-1] and fmt[0] in ('"', "'")):
        msg = "%r tag's argument should be in quotes" % tag_name
        raise template.TemplateSyntaxError(msg)

    return CurrentTimeNode3(fmt[1:-1], var_name)

Now do_current_time() passes the format string and the variable name to CurrentTimeNode3 .

现在 do_current_time() 把格式字符串和变量名传递给 CurrentTimeNode3

Parsing Until Another Block Tag
分析直至另一个块标签

Template tags can work as blocks containing other tags (think {% if %} , {% for %} , etc.). To create a template tag like this, use parser.parse() in your compilation function.

模板标签可以像包含其它标签的块一样工作(想想 {% if %}{% for %} 等)。 要创建一个这样的模板标签,在你的编译函数中使用 parser.parse()

Heres how the standard {% comment %} tag is implemented:

标准的 {% comment %} 标签是这样实现的:

def do_comment(parser, token):
    nodelist = parser.parse(('endcomment',))
    parser.delete_first_token()
    return CommentNode()

class CommentNode(template.Node):
    def render(self, context):
        return ''

parser.parse() takes a tuple of names of block tags to parse until. It returns an instance of django.template.NodeList , which is a list of all Node objects that the parser encountered before it encountered any of the tags named in the tuple.

parser.parse() 接收一个包含了需要分析块标签名的元组作为参数. 它返回一个 django.template.NodeList 实例, 它是一个包含了所有 Node 对象的列表,这些对象代表了分析器在遇到元组中任一标签名之 的内容.

So in the preceding example, nodelist is a list of all nodes between {% comment %} and {% endcomment %} , not counting {% comment %} and {% endcomment %} themselves.

因此在前面的例子中, nodelist 是在 {% comment %}{% endcomment %} 之间所有节点的列表,不包括 {% comment %}{% endcomment %} 自身。

After parser.parse() is called, the parser hasnt yet consumed the {% endcomment %} tag, so the code needs to explicitly call parser.delete_first_token() to prevent that tag from being processed twice.

parser.parse() 被调用之后,分析器还没有清除 {% endcomment %} 标签,因此代码需要显式地调用 parser.delete_first_token() 来防止该标签被处理两次。

Then CommentNode.render() simply returns an empty string. Anything between {% comment %} and {% endcomment %} is ignored.

之后 CommentNode.render() 只是简单地返回一个空字符串。在 {% comment %}{% endcomment %} 之间的所有内容都被忽略。

Parsing Until Another Block Tag and Saving Contents
分析直至另外一个块标签并保存内容

In the previous example, do_comment() discarded everything between {% comment %} and {% endcomment %} . Its also possible to do something with the code between block tags instead.

在前一个例子中, do_comment() 抛弃了在 {% comment %}{% endcomment %} 之间的所有内容。同样,也可以对块标签之间的代码进行处理。

For example, heres a custom template tag, {% upper %} , that capitalizes everything between itself and {% endupper %} :

例如,这个自定义模板标签: {% upper %} ,它把自己和 {% endupper %} 之间的所有内容都变成大写:

{% upper %}
    This will appear in uppercase, {{ your_name }}.
{% endupper %}

As in the previous example, well use parser.parse() . This time, we pass the resulting nodelist to Node :

就像前面的例子一样,我们将使用 parser.parse() 。这次,我们将产生的 nodelist 传递给 Node

@register.tag
def do_upper(parser, token):
    nodelist = parser.parse(('endupper',))
    parser.delete_first_token()
    return UpperNode(nodelist)

class UpperNode(template.Node):

    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        output = self.nodelist.render(context)
        return output.upper()

The only new concept here is self.nodelist.render(context) in UpperNode.render() . This simply calls render() on each Node in the node list.

这里唯一的一个新概念是 UpperNode.render() 中的 self.nodelist.render(context) 。它对节点列表中的每个 Node 简单的调用 render()

For more examples of complex rendering, see the source code for {% if %} , {% for %} , {% ifequal %} , and {% ifchanged %} . They live in django/template/defaulttags.py .

更多的复杂渲染示例请查看 django/template/defaulttags.py 中的 {% if %}{% for %}{% ifequal %}{% ifchanged %} 的代码。

Shortcut for Simple Tags

简单标签的快捷方式

Many template tags take a single argumenta string or a template variable referenceand return a string after doing some processing based solely on the input argument and some external information. For example, the current_time tag we wrote earlier is of this variety. We give it a format string, and it returns the time as a string.

许多模板标签接收单一的字符串参数或者一个模板变量 引用,然后独立地根据输入变量和一些其它外部信息进行处理并返回一个字符串. 例如, 我们先前写的 current_time 标签就是这样一个例子. 我们给它格式字符串, 然后它把时间作为字符串返回.

To ease the creation of these types of tags, Django provides a helper function, simple_tag . This function, which is a method of django.template.Library , takes a function that accepts one argument, wraps it in a render function and the other necessary bits mentioned previously, and registers it with the template system.

为了简化这类标签,Django 提供了一个帮助函数: simple_tag 。这个函数是 django.template.Library 的一个方法,它接受一个只有一个参数的函数作参数,把它包装在 render 函数和之前提及过的其他的必要单位中,然后通过模板系统注册标签。

Our earlier current_time function could thus be written like this:

我们之前的的 current_time 函数于是可以写成这样:

def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

register.simple_tag(current_time)

In Python 2.4, the decorator syntax also works:

在Python 2.4中,也可以使用修饰语法:

@register.simple_tag
def current_time(token):
    ...

A couple of things to notice about the simple_tag helper function are as follows:

有关 simple_tag 辅助函数,需要注意下面一些事情:

  • Only the (single) argument is passed into our function.

  • 传递给我们的函数的只有(单个)参数。

  • Checking for the required number of arguments has already been done by the time our function is called, so we dont need to do that.

  • 在我们的函数被调用的时候,检查必需参数个数的工作已经完成了,所以我们不需要再做这个工作。

  • The quotes around the argument (if any) have already been stripped away, so we receive a plain string.

  • 参数两边的引号(如果有的话)已经被截掉了,所以我们会接收到一个普通字符串。

Inclusion Tags

包含标签

Another common template tag is the type that displays some data by rendering another template. For example, Djangos admin interface uses custom template tags to display the buttons along the bottom of the add/change form pages. Those buttons always look the same, but the link targets change depending on the object being edited. Theyre a perfect case for using a small template that is filled with details from the current object.

另外一类常用的模板标签是通过渲染 其他 模板显示数据的。比如说,Django的后台管理界面,它使用了自定义的模板标签来显示新增/编辑表单页面下部的按钮。那些按钮看起来总是一样的,但是链接却随着所编辑的对象的不同而改变。这就是一个使用小模板很好的例子,这些小模板就是当前对象的详细信息。

These sorts of tags are called inclusion tags . Writing inclusion tags is probably best demonstrated by example. Lets write a tag that produces a list of choices for a simple multiple-choice Poll object. Well use the tag like this:

这些排序标签被称为 包含标签 。如何写包含标签最好通过举例来说明。我们来写一个可以生成一个选项列表的多选项对象 Poll 。标签这样使用:

{% show_results poll %}

The result will be something like this:

结果将会像下面这样:

<ul>
  <li>First choice</li>
  <li>Second choice</li>
  <li>Third choice</li>
</ul>

First, we define the function that takes the argument and produces a dictionary of data for the result. Notice that we need to return only a dictionary, not anything more complex. This will be used as the context for the template fragment:

首先,我们定义一个函数,通过给定的参数生成一个字典形式的结果。 需要注意的是,我们只需要返回字典类型的结果就行了,它将被用做模板片断的context。 (译注:dict 的 key 作为变量名在模板中被使用)

def show_books_for_author(author):
    books = author.book_set.all()
    return {'books': books}

Next, we create the template used to render the tags output. Following our example, the template is very simple:

接下来,我们创建用于渲染标签输出的模板。在我们的例子中,模板很简单:

<ul>
{% for book in books %}
    <li> {{ book }} </li>
{% endfor %}
</ul>

Finally, we create and register the inclusion tag by calling the inclusion_tag() method on a Library object.

最后,我们通过对一个 Library 对象使用 inclusion_tag() 方法来创建并注册这个包含标签。

Following our example, if the preceding template is in a file called polls/result_snippet.html , we register the tag like this:

在我们的例子中,如果先前的模板在 polls/result_snippet.html 文件中,那么我们这样注册标签:

register.inclusion_tag('books/books_for_author.html')(show_books_for_author)

As always, Python 2.4 decorator syntax works as well, so we could have instead written this:

和往常一样,我们也可以使用Python 2.4中的修饰语法,所以我们还可以这么写:

@register.inclusion_tag('books/books_for_author.html')
def show_books_for_author(show_books_for_author):
    ...

Sometimes, your inclusion tags need access to values from the parent templates context. To solve this, Django provides a takes_context option for inclusion tags. If you specify takes_context in creating a template tag, the tag will have no required arguments, and the underlying Python function will have one argument: the template context as of when the tag was called.

有时候,你的包含标签需要访问父模板的context。为了解决这个问题,Django提供了一个 takes_context 选项。如果你在创建模板标签时,指明了这个选项,这个标签就不需要参数,并且下面的Python函数会带一个参数:就是当这个标签被调用时的模板context。

For example, say youre writing an inclusion tag that will always be used in a context that contains home_link and home_title variables that point back to the main page. Heres what the Python function would look like:

例如,你正在写一个包含标签,该标签包含有指向主页的 home_linkhome_title 变量。Python函数会像这样:

@register.inclusion_tag('link.html', takes_context=True)
def jump_link(context):
    return {
        'link': context['home_link'],
        'title': context['home_title'],
    }

Note

备注

The first parameter to the function must be called context .

函数的第一个参数 必须context

The template link.html might contain the following:

模板 link.html 可能包含下面的东西:

Jump directly to <a href="{{ link }}">{{ title }}</a>.

Then, anytime you want to use that custom tag, load its library and call it without any arguments, like so:

然后您想使用自定义标签时,就可以加载它的库,然后不带参数地调用它,就像这样:

{% jump_link %}

Writing Custom Template Loaders

编写自定义模板加载器

Djangos built-in template loaders (described in the Inside Template Loading section above) will usually cover all your template-loading needs, but its pretty easy to write your own if you need special loading logic. For example, you could load templates from a database, or directly from a Subversion repository using Subversions Python bindings, or (as shown shortly) from a ZIP archive.

Djangos 内置的模板加载器(在先前的模板加载内幕章节有叙述)通常会满足你的所有的模板加载需求,但是如果你有特殊的加载需求的话,编写自己的模板加载器也会相当简单。比如:你可以从数据库加载模板,或者使用 Subversions的Python实现直接从Subversion库加载模板,再或者(稍后展示)从zip文件加载模板。

A template loaderthat is, each entry in the TEMPLATE_LOADERS setting is expected to be a callable with this interface:

一个模板加载器,也就是 TEMPLATE_LOADERS 中的每一项,都要能被下面这个接口所调用:

load_template_source(template_name, template_dirs=None)

The template_name argument is the name of the template to load (as passed to loader.get_template() or loader.select_template() ), and template_dirs is an optional list of directories to search instead of TEMPLATE_DIRS .

参数 template_name 是所加载模板的名称 (和传递给 loader.get_template() 或者 loader.select_template() 一样), 而 template_dirs 是一个可选的包含除去 TEMPLATE_DIRS 之外的搜索目录列表。

If a loader is able to successfully load a template, it should return a tuple: (template_source, template_path) . Here, template_source is the template string that will be compiled by the template engine, and template_path is the path the template was loaded from. That path might be shown to the user for debugging purposes, so it should quickly identify where the template was loaded from.

如果加载器能够成功加载一个模板, 它应当返回一个元组: (template_source, template_path) 。在这里的 template_source 就是将被模板引擎编译的的模板字符串,而 template_path 是被加载的模板的路径。由于那个路径可能会出于调试目的显示给用户,因此它应当很快的指明模板从哪里加载而来。

If the loader is unable to load a template, it should raise django.template.TemplateDoesNotExist .

如果加载器加载模板失败,那么就会触发 django.template.TemplateDoesNotExist 异常。

Each loader function should also have an is_usable function attribute. This is a Boolean that informs the template engine whether this loader is available in the current Python installation. For example, the eggs loader (which is capable of loading templates from Python eggs) sets is_usable to False if the pkg_resources module isnt installed, because pkg_resources is necessary to read data from eggs.

每个加载函数都应该有一个名为 is_usable 的函数属性。这个属性是一个布尔值,用于告知模板引擎这个加载器是否在当前安装的Python中可用。例如,如果 pkg_resources 模块没有安装的话,eggs加载器(它能够从python eggs中加载模板)就应该把 is_usable 设为 False ,因为必须通过 pkg_resources 才能从eggs中读取数据。

An example should help clarify all of this. Heres a template loader function that can load templates from a ZIP file. It uses a custom setting, TEMPLATE_ZIP_FILES , as a search path instead of TEMPLATE_DIRS , and it expects each item on that path to be a ZIP file containing templates:

一个例子可以清晰地阐明一切。这儿是一个模板加载函数,它可以从ZIP文件中加载模板。它使用了自定义的设置 TEMPLATE_ZIP_FILES 来取代了 TEMPLATE_DIRS 用作查找路径,并且它假设在此路径上的每一个文件都是包含模板的ZIP文件:

import zipfile
from django.conf import settings
from django.template import TemplateDoesNotExist

def load_template_source(template_name, template_dirs=None):
    """Template loader that loads templates from a ZIP file."""

    template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])

    # Try each ZIP file in TEMPLATE_ZIP_FILES.
    for fname in template_zipfiles:
        try:
            z = zipfile.ZipFile(fname)
            source = z.read(template_name)
        except (IOError, KeyError):
            continue
        z.close()
        # We found a template, so return the source.
        template_path = "%s:%s" % (fname, template_name)
        return (source, template_path)

    # If we reach here, the template couldn't be loaded
    raise TemplateDoesNotExist(template_name)

# This loader is always usable (since zipfile is included with Python)
load_template_source.is_usable = True

The only step left if we want to use this loader is to add it to the TEMPLATE_LOADERS setting. If we put this code in a package called mysite.zip_loader , then we add mysite.zip_loader.load_template_source to TEMPLATE_LOADERS .

我们要想使用它,还差最后一步,就是把它加入到 TEMPLATE_LOADERS 。如果我们把这部分代码放到一个叫做 mysite.zip_loader 的包中,我们就需要把 mysite.zip_loader.load_template_source 加入到 TEMPLATE_LOADERS 中去。

Using the Built-in Template Reference

使用内置的模板参考

Djangos admin interface includes a complete reference of all template tags and filters available for a given site. Its designed to be a tool that Django programmers give to template developers. To see it, go to the admin interface and click the Documentation link at the upper right of the page.

Django管理界面包含一个完整的参考资料,里面有所有的可以在特定网站上使用的模板标签和过滤器。它设计的初衷是Django程序员提供给模板开发人员的一个工具。你可以点击管理页面右上角的文档链接来查看这些资料。

The reference is divided into four sections: tags, filters, models, and views. The tags and filters sections describe all the built-in tags (in fact, the tag and filter references in Chapter 4 come directly from those pages) as well as any custom tag or filter libraries available.

参考说明分为4个部分:标签、过滤器、模型和视图。 标签过滤器 部分描述了所有内置的标签(实际上,第4章中用到的标签和过滤器都直接来源于那几页)以及一些可用的自定义标签和过滤器库。

The views page is the most valuable. Each URL in your site has a separate entry here. If the related view includes a docstring, clicking the URL will show you the following:

视图 页面是最有价值的。网站中的每个URL都在这儿有独立的入口。如果相关的视图包含一个 文档字符串, 点击URL,你就会看到:

  • The name of the view function that generates that view

  • 生成本视图的视图函数的名字

  • A short description of what the view does

  • 视图功能的一个简短描述

  • The context, or a list of variables available in the views template

  • 上下文或一个视图模板中可用的变量的列表

  • The name of the template or templates that are used for that view

  • 视图使用的模板的名字

For a detailed example of view documentation, read the source code for Djangos generic object_list view, which is in django/views/generic/list_detail.py .

要想查看关于视图文档的更详细的例子,请阅读Django的通用 object_list 视图部分的源代码,它位于 django/views/generic/list_detail.py 文件中。

Because Django-powered sites usually use database objects, the models pages describe each type of object in the system along with all the fields available on that object.

通常情况下,由Django构建的网站都会使用数据库对象, 模型 页面描述了系统中所有类型的对象,以及该对象对应的所有可用字段。

Taken together, the documentation pages should tell you every tag, filter, variable, and object available to you in a given template.

总之,这些文档告诉你在模板中的所有可用的标签、过滤器、变量和对象。

Configuring the Template System in Standalone Mode

配置独立模式下的模板系统

Note

备注

This section is only of interest to people trying to use the template system as an output component in another application. If you are using the template system as part of a Django application, the information presented here doesnt apply to you.

这部分只针对于对在其他应用中使用模版系统作为输出组件感兴趣的人。如果你是在Django应用中使用模版系统,请略过此部分。

Normally, Django will load all the configuration information it needs from its own default configuration file, combined with the settings in the module given in the DJANGO_SETTINGS_MODULE environment variable. But if youre using the template system independently of the rest of Django, the environment variable approach isnt very convenient, because you probably want to configure the template system in line with the rest of your application rather than dealing with settings files and pointing to them via environment variables.

通常,Django会从它的默认配置文件和由 DJANGO_SETTINGS_MODULE 环境变量所指定的模块中加载它需要的所有配置信息。但是当你想在非Django应用中使用模版系统的时候,采用环境变量并不是很好的方法。比起为模版系统单独采用配置文件并用环境变量来指向它,你可能更希望能够在你的应用中采用一致的配置方法来配置模版系统和其他部分

To solve this problem, you need to use the manual configuration option described fully Appendix E. In a nutshell, you need to import the appropriate pieces of the template system and then, before you call any of the template functions, call django.conf.settings.configure() with any settings you wish to specify.

为了解决这个问题,你需要使用附录E中所描述的手动配置选项。简单来说,你需要引入合适的模板系统,并且在调用任何模板函数 之前 调用 django.conf.settings.configure() 来指定任何你想要的设置。

You might want to consider setting at least TEMPLATE_DIRS (if you are going to use template loaders), DEFAULT_CHARSET (although the default of utf-8 is probably fine), and TEMPLATE_DEBUG . All available settings are described in Appendix E, and any setting starting with TEMPLATE_ is of obvious interest.

你可能会考虑至少要设置 TEMPLATE_DIRS (如果你打算使用模板加载器), DEFAULT_CHARSET (尽管默认的 utf-8 编码相当好用),以及 TEMPLATE_DEBUG 。所有可用的选项都在附录E中详细描述,所有以 TEMPLATE_ 开头的选项都可能使你感兴趣的。

Whats Next

接下来?

So far this book has assumed that the content youre displaying is HTML. This isnt a bad assumption for a book about Web development, but at times youll want to use Django to output other data formats.

迄今为止,本书假定您想展示的内容为HTML。对于一个有关Web开发的书来说,这不是一个 不好的假设,但有时你想用Django输出其他数据格式。

The next chapter describes how you can use Django to produce images, PDFs, and any other data format you can imagine.

下一章将讲解如何使用Django生成图像、PDF、还有你可以想到的其他数据格式。

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by py3k.cn. 粤ICP备16122281号-1