API

优质
小牛编辑
113浏览
2023-12-01

本文档描述 Jinja2 的 API 而不是模板语言。这对实现模板接口,而非创建 Jinja2 模板,是最有用的参考,

基础

Jinja2 使用一个名为 Environment 的中心对象。这个类的实例用于存储配 置、全局对象,并用于从文件系统或其它位置加载模板。即使你通过:class:Template 类的构造函数用字符串创建模板,也会为你自动创建一个环境,尽管是共享的。

大多数应用在应用初始化时创建一个 Environment 对象,并用它加载模板。 在某些情况下,如果使用多份配置,使用并列的多个环境无论如何是有用的。

配置 Jinja2 为你的应用加载文档的最简单方式看起来大概是这样:

from jinja2 import Environment, PackageLoader
env = Environment(loader=PackageLoader('yourapplication', 'templates'))

这会创建一个默认设定下的模板环境和一个在 yourapplication python 包中的 templates 文件夹中寻找模板的加载器。多个加载器是可用的,如果你需要从 数据库或其它资源加载模板,你也可以自己写一个。

你只需要调用 get_template() 方法从这个环境中加载模板,并会返回已加载的 Template:

template = env.get_template('mytemplate.html')

用若干变量来渲染它,调用 render() 方法:

print template.render(the='variables', go='here')

使用一个模板加载器,而不是向 TemplateEnvironment.from_string() 传递字符串,有许多好处。除了使用上便利, 也使得模板继承成为可能。

Unicode

Jinja2 内部使用 Unicode ,这意味着你需要向渲染函数传递 Unicode 对象或只包含 ASCII 字符的字符串。此外,换行符按照默认 UNIX 风格规定行序列结束( \n )。

Python 2.x 支持两种表示字符串对象的方法。一种是 str 类型,另一种是 unicode 类型,它们都继承于 basestring 类型。不幸的是,默认的 str 不 应该用于存储基于文本的信息,除非只用到 ASCII 字符。在 Python 2.6 中,可以 在模块层指定 unicode 为默认值,而在 Python 3 中会是默认值。

要显式使用一个 Unicode 字符串,你需要给字符串字面量加上 u 前缀: u'Hänsel und Gretel sagen Hallo' 。这样 Python 会用当前模块的字符编码来 解码字符串,来把字符串存储为 Unicode 。如果没有指定编码,默认是 ASCII , 这意味着你不能使用任何非 ASCII 的标识符。

在使用 Unicode 字面量的 Python 模块的首行或第二行添加下面的注释,来妥善设 置模块编码:

# -*- coding: utf-8 -*-

我们推荐为 Python 模块和模板使用 utf-8 编码,因为在 utf-8 中,可以表示 Unicode 中的每个字符,并且向后兼容 ASCII 。对于 Jinja2 ,模板的默认编码 假定为 utf-8 。

用 Jinja2 来处理非 Unicode 数据是不可能的。这是因为 Jinja2 已经在语言层 使用了 Unicode 。例如 Jinja2 在表达式中把不间断空格视为有效的空格,这需要 获悉编码或操作一个 Unicode 字符串。

关于 Python 中 Unicode 的更多细节,请阅读完善的 Unicode documentation

另一件重要的事情是 Jinja2 如何处理模板中的字符串字面量。原生实现会对所有 字符串字面量使用 Unicode ,但在过去这是有问题的,因为一些库显式地检查它 们的类型是否为 str 。例如 datetime.strftime 不接受 Unicode 参数。 为了不彻底破坏它, Jinja2 对只有 ASCII 的字符串返回 str,而对其它返回 unicode:

>>> m = Template(u"{% set a, b = 'foo', 'föö' %}").module
>>> m.a
'foo'
>>> m.b
u'f\xf6\xf6'

高层 API

高层 API 即是你会在应用中用于加载并渲染模板的 API 。 低层 API 相反,只在你想深入挖掘 Jinja2 或 开发扩展 时有用。

class jinja2.Environment([options])

New in version 2.4.

从 Jinja 2.4 开始,自动转义的首选途径就是启用 自动转义扩展 并为自动转义配置一个合适的默认值。这使得在单个模板基础上开关自动转义成为 可能(比如 HTML 对 文本)

这里推荐为以 .html.htm.xml 以及 .xhtml 的模板开启 自动转义 ,并对所有其它扩展名禁用:

def guess_autoescape(template_name):
    if template_name is None or '.' not in template_name:
        return False
    ext = template_name.rsplit('.', 1)[1]
    return ext in ('html', 'htm', 'xml')

env = Environment(autoescape=guess_autoescape,
                  loader=PackageLoader('mypackage'),
                  extensions=['jinja2.ext.autoescape'])

假设实现一个自动转义函数,确保你也视 None 为有效模板名接受。这会在从字符 串生成模板时传递。

可以用 autoescape 块在模板内临时地更改这种行为。(见 自动转义扩展 )。

标识符的说明

Jinja2 使用正规的 Python 2.x 命名规则。有效的标识符必须匹配 [a-zA-Z_][a-zA-Z0-9_]* 。事实上,当前不允许非 ASCII 字符。这个限制可能 会在 Python 3 充分规定 unicode 标识符后消失。

过滤器和测试会在独立的命名空间中查找,与标识符语法有细微区别。过滤器和测 试可以包含点,用于按主题给过滤器和测试分组。例如,把一个名为 to.unicode 的函数添加到过滤器字典是完全有效的。过滤器和测试标识符的正则表达式是 [a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*

未定义类型

这些类可以用作未定义类型。 Environment 的构造函数接受一个可以是 那些类或一个 Undefined 的自定义子类的 undefined 参数。无论何时, 这些对象创建或返回时,模板引擎都不能查出其名称或访问其属性。未定义值上的 某些操作之后是允许的,而其它的会失败。

最接近常规 Python 行为的是 StrictUndefined ,如果它是一个未定义对象, 它不允许除了测试之外的一切操作。

class jinja2.Undefined
class jinja2.runtime.Context

加载器负责从诸如文件系统的资源加载模板。环境会把编译的模块像 Python 的 sys.modules 一样保持在内存中。与 sys.models 不同,无论如何这个 缓存默认有大小限制,且模板会自动重新加载。 所有的加载器都是 BaseLoader 的子类。如果你想要创建自己的加载器,继 承 BaseLoader 并重载 get_source

class jinja2.BaseLoader

Jinja 2.1 和更高的版本支持外部字节码缓存。字节码缓存使得在首次使用时把生成的字节码 存储到文件系统或其它位置来避免处理模板。

这在当你有一个在首个应用初始化的 web 应用, Jinja 一次性编译大量模板拖慢应用时尤其 有用。

要使用字节码缓存,把它实例化并传给 Environment

class jinja2.BytecodeCache

这些辅助函数和类在你向 Jinja2 环境中添加自定义过滤器或函数时很有用。

jinja2.environmentfilter(f)
exception jinja2.TemplateError(message=None)

自定义过滤器只是常规的 Python 函数,过滤器左边作为第一个参数,其余的参数作 为额外的参数或关键字参数传递到过滤器。

例如在过滤器 {{ 42|myfilter(23) }} 中,函数被以 myfilter(42, 23) 调 用。这里给出一个简单的过滤器示例,可以应用到 datetime 对象来格式化它们:

def datetimeformat(value, format='%H:%M / %d-%m-%Y'):
    return value.strftime(format)

你可以更新环境上的 filters 字典来把它注册到模板环境上:

environment.filters['datetimeformat'] = datetimeformat

在模板中使用如下:

written on: {{ article.pub_date|datetimeformat }}
publication date: {{ article.pub_date|datetimeformat('%d-%m-%Y') }}

也可以传给过滤器当前模板上下文或环境。当过滤器要返回一个未定义值或检查当前的 autoescape 设置时很有用。为此,有三个装饰器: environmentfilter()contextfilter()evalcontextfilter()

这里是一个小例子,过滤器把一个文本在 HTML 中换行或分段,并标记返回值为安全 的 HTML 字符串,因为自动转义是启用的:

import re
from jinja2 import evalcontextfilter, Markup, escape

_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')

@evalcontextfilter
def nl2br(eval_ctx, value):
    result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
                          for p in _paragraph_re.split(escape(value)))
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

上下文过滤器工作方式相同,只是第一个参数是当前活动的 Context 而 不是环境。

求值上下文

求值上下文(缩写为 eval context 或 eval ctx )是 Jinja 2.4 中引入的新对象, 并可以在运行时激活/停用已编译的特性。

当前它只用于启用和禁用自动转义,但也可以用于扩展。

在之前的 Jinja 版本中,过滤器和函数被标记为环境可调用的来从环境中检查自动 转义的状态。在新版本中鼓励通过求值上下文来检查这个设定。

之前的版本:

@environmentfilter
def filter(env, value):
    result = do_something(value)
    if env.autoescape:
        result = Markup(result)
    return result

在新版本中,你可以用 contextfilter() 从实际的上下文中访问求值上下 文,或用 evalcontextfilter() 直接把求值上下文传递给函数:

@contextfilter
def filter(context, value):
    result = do_something(value)
    if context.eval_ctx.autoescape:
        result = Markup(result)
    return result

@evalcontextfilter
def filter(eval_ctx, value):
    result = do_something(value)
    if eval_ctx.autoescape:
        result = Markup(result)
    return result

求值上下文一定不能在运行时修改。修改只能在扩展中的 用 nodes.EvalContextModifiernodes.ScopedEvalContextModifier 发生,而不是通过求值上下文对 象本身。

class jinja2.nodes.EvalContext(environment, template_name=None)

测试像过滤器一样工作,只是测试不能访问环境或上下文,并且它们不能链式使用。 测试的返回值应该是 TrueFalse 。测试的用途是让模板设计者运行类型和 一致性检查。

这里是一个简单的测试,检验一个变量是否是素数:

import math

def is_prime(n):
    if n == 2:
        return True
    for i in xrange(2, int(math.ceil(math.sqrt(n))) + 1):
        if n % i == 0:
            return False
    return True

你可以通过更新环境上的 tests 字典来注册它:

environment.tests['prime'] = is_prime

模板设计者可以在之后这样使用测试:

{% if 42 is prime %}
    42 is a prime number
{% else %}
    42 is not a prime number
{% endif %}

全局命名空间

Environment.globals 字典中的变量是特殊的,它们对导入的模板也是可用的, 即使它们不通过上下文导入。这是你可以放置始终可访问的变量和函数的地方。此外, Template.globals 是那些对特定模板可用的变量,即对所有的 render() 调用可用。

低层 API

低层 API 暴露的功能对理解一些实现细节、调试目的或高级 扩展 技巧是有用的。除非你准确地了解你在做什么,否则 不推荐使用这些 API 。

Environment.lex(source, name=None, filename=None)

New in version 2.2.

元 API 返回一些关于抽象语法树的信息,这些信息能帮助应用实现更多的高级模板概 念。所有的元 API 函数操作一个 Environment.parse() 方法返回的抽象语法 树。

jinja2.meta.find_undeclared_variables(ast)

Returns a set of all variables in the AST that will be looked up from the context at runtime. Because at compile time it’s not known which variables will be used depending on the path the execution takes at runtime, all variables are returned.

>>> from jinja2 import Environment, meta
>>> env = Environment()
>>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
>>> meta.find_undeclared_variables(ast)
set(['bar'])

Implementation

Internally the code generator is used for finding undeclared variables. This is good to know because the code generator might raise a TemplateAssertionError during compilation and as a matter of fact this function can currently raise that exception as well.

jinja2.meta.find_referenced_templates(ast)

Finds all the referenced templates from the AST. This will return an iterator over all the hardcoded template extensions, inclusions and imports. If dynamic inheritance or inclusion is used, None will be yielded.

>>> from jinja2 import Environment, meta
>>> env = Environment()
>>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
>>> list(meta.find_referenced_templates(ast))
['layout.html', None]

This function is useful for dependency tracking. For example if you want to rebuild parts of the website after a layout template has changed.