模板设计器文档

这份文档描述了模板引擎中的语法和语义结构,对于创建 Jinja 模板是一份相当有用 的参考。 因为模板引擎非常灵活,应用中的配置会在分隔符和未定义值的行为方面与 这里的配置有细微差异。

概要

Jinja的模板就是文本文件。 它可以生成任何基于文本的格式(HTML、XML、CSV、LaTex 等等)。 它并没有特定的扩展名, .html.xml 都是可以的。

模板包含变量表达式标签。在渲染的时候,变量和表达式将被替换为对应的值,而标签用来控制模板的逻辑。模板语法的灵感大量来自于 Django 和 Python 。

下面是一个最基本的模板,它将展示Jinja的一些最基本的用法。 我们会在文档中后面的部分解释细节:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>My Webpage</title>
</head>
<body>
    <ul id="navigation">
    {% for item in navigation %}
        <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
    {% endfor %}
    </ul>

    <h1>My Webpage</h1>
    {{ a_variable }}

    {##}
</body>
</html>

这包含了默认的设定。 应用开发者也会把语法从 {% templatetag openblock %} foo {% templatetag closeblock %} 改成 <% foo %> 或类似的东西。

有几种分隔符。默认的Jinja分隔符配置如下:

变量

应用把变量传递到模板,你可能在模板中弄混。

如果应用程序传递的模板中的变量,您可以混淆它们。 变量上面也可以有你能访问的属性或元 素。 变量的属性很大程度上取决于提供该变量的应用程序。

除了标准Python __getitem__“下标”语法([])之外,还可以使用点(.)访问变量的属性。 。

下面的几行效果是一样的:

{{ foo.bar }}
{{ foo['bar'] }}

知晓花括号 不是 变量的一部分,而是打印语句的一部分是重要的。 如果你访问标签 里的不带括号的变量。

如果变量或属性不存在,会返回一个未定义值。 你可以对这类值做什么取决于应用的配 置,默认的行为是它如果被打印,其求值为一个空字符串,并且你可以迭代它,但其它 操作会失败。

实施

为方便起见,Jinja2 中 foo.bar 在 Python 层中做下面的事情:

  • check for an attribute called bar on foo (getattr(foo, 'bar'))
  • 如果没有,请检查foofoo.__getitem__('bar')中的项目'bar'
  • 如果没有,返回一个未定义对象。

foo['bar'] 的方式相反,只在顺序上有细小差异:

  • 检查在 foo 中是否有一个 'bar' 项。 foo.__getitem__('bar')
  • 如果没有,检查 foo 上是否有一个名为 bar 的属性。 getattr(foo, 'bar')
  • 如果没有,返回一个未定义对象。

如果一个对象有同名的项和属性,这很重要。 此外,有一个 attr() 过滤 器,它只查找属性。

过滤器

变量可以通过 过滤器 修改。 过滤器与变量用管道符号( | )分割,并且也 可以用圆括号传递可选参数。 可以链接多个过滤器。 多个过滤器可以链式调用,前一个过滤器的输出会被作为 后一个过滤器的输入。

例如,{{ name | striptags | title }}将从变量name和title-case输出(title(striptags(name)))。

过滤器接受带圆括号的参数,如同函数调用。 例如:{{ listx | join(', ') }}将使用逗号(str.join(', ', listx)加入列表。

下面的 内置过滤器清单 节介绍了所有的内置过滤器。

测试

除了过滤器,所谓的“测试”也是可用的。 测试可以用于对照普通表达式测试一个变量。 要测试一个变量或表达式,你要在变量后加上一个 is 以及测试的名称。 例如,要得出 一个值是否定义过,你可以用 name is defined ,这会根据 name 是否定义返回 true 或 false 。

测试也可以接受参数。 如果测试只接受一个参数,你可以省去括号来分组它们。 例如, 下面的两个表达式做同样的事情:

{% if loop.index is divisibleby 3 %}
{% if loop.index is divisibleby(3) %}

下面的 内置测试清单 章节介绍了所有的内置测试。

评论

要注释掉模板中的一部分行,请使用默认设置为的注释语法{# ... #} / t3> 这在调试或 添加给你自己或其它模板设计者的信息时是有用的:

{# note: commented-out template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}

空白控件

在默认配置中:

  • 如果存在,则去除单个尾随换行符
  • 默认配置中,模板引擎不会对空白做进一步修改,所以每个空白(空格、制表符、换行符 等等)都会原封不动返回。 返回不变

如果应用配置了 Jinja 的 trim_blocks ,模板标签后的 第一个换行符会被自动移除(像 PHP 中一样)。 还可以将lstrip_blocks选项设置为将制表符和空格从一行的开始到一个块的开头。(如果在块开始之前还有其他字符,则不会删除任何内容。)

在启用trim_blockslstrip_blocks的情况下,可以将块标记放在其自己的行上,整个块行在呈现时将被删除,保留内容的空格。例如,如果没有trim_blockslstrip_blocks选项,则此模板:

<div>
    {% if True %}
        yay
    {% endif %}
</div>

被渲染与div内的空白行:

<div>

        yay

</div>

但是,如果启用trim_blockslstrip_blocks,模板块行将被删除,并保留其他空格:

<div>
        yay
</div>

您可以通过在块的开头放置加号(+)来手动禁用lstrip_blocks行为:

<div>
        {%+ if something %}yay{% endif %}
</div>

此外,你也可以手动剥离模板中的空白。 如果在块的开始或结束处添加减号(-)(例如For标记),注释或变量表达式,之后该块将被删除:

{% for item in seq -%}
    {{ item }}
{%- endfor %}

这会产出中间不带空白的所有元素。 如果 seq19 的数字的列表, 输出会是 123456789

如果开启了 行语句 ,它们会自动去除行首的空白。

默认情况下,Jinja2也删除尾随换行符。要保留单个尾随换行符,请将Jinja配置为keep_trailing_newline

注意

标签和减号之间不能有空白。

有效的:

{%- if foo -%}...{% endif %}

无效的:

{% - if foo - %}...{% endif %}

正在转义

有时想要或甚至必要让 Jinja 忽略部分,不会把它作为变量或块来处理。 例如,如果 使用默认语法,你想在在使用把 {% templatetag openvariable %} 作为原始字符串使用,并且不会开始一个变量 的语法结构,你需要使用一个技巧。

最简单的方法是在变量分隔符中( {% templatetag openvariable %} )使用变量表达式输出:

{{ '{{' }}

对于较大的段落,标记一个块为 raw 是有意义的。 例如展示 Jinja 语法的实例, 你可以在模板中用这个片段:

{% raw %}
    <ul>
    {% for item in seq %}
        <li>{{ item }}</li>
    {% endfor %}
    </ul>
{% endraw %}

行语句

如果应用启用了行语句,就可以把一个行标记为一个语句。 例如如果行语句前缀配置为 # ,下面的两个例子是等价的:

<ul>
# for item in seq
    <li>{{ item }}</li>
# endfor
</ul>

<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>

行语句前缀可以出现在一行的任意位置,只要它前面没有文本。 为了语句有更好的可读 性,在块的开始(比如 forifelif 等等)以冒号结尾: 可能以冒号结尾:

# for item in seq:
    ...
# endfor

注意

若有未闭合的圆括号、花括号或方括号,行语句可以跨越多行:

<ul>
# for href, caption in [('index.html', 'Index'),
                        ('about.html', 'About')]:
    <li><a href="{{ href }}">{{ caption }}</a></li>
# endfor
</ul>

从 Jinja 2.2 开始,行注释也可以使用了。 例如如果配置 ## 为行注释前缀, 行中所有 ## 之后的内容(不包括换行符)会被忽略:

# for item in seq:
    <li>{{ item }}</li>     ## this comment is ignored
# endfor

模板继承

Jinja 中最强大的部分就是模板继承。 模板继承允许你构建一个包含你站点共同元素的基 本模板“骨架”,并定义子模板可以覆盖的

听起来复杂,实际上很简单。 从例子上手是最易于理解的。

基本模板

这个模板,我们会把它叫做 base.html ,定义了一个简单的 HTML 骨架文档,你可 能使用一个简单的两栏页面。 用内容填充空的块是子模板的工作:

<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>

在这个例子中,{% block %}标签定义了子模板可以填充的四个块。 所有的 block 标签 告诉模板引擎子模板可以覆盖模板中的这些部分。

子模板

子模板可能如下所示:

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome to my awesome homepage.
    </p>
{% endblock %}

标签是这里的关键,{% 延伸 %} 它告诉模板引擎这个模板“继承”另一个模板。 当模板系统对这个模板求值时,首先定位父模板。 extends 标签应该是模板中的第一个 标签。 它前面的所有东西都会按照普通情况打印出来,而且可能会导致一些困惑。 更多 该行为的细节以及如何利用它,见 Null-Master 退回

模板的文件名依赖于模板加载器。 例如 FileSystemLoader 允许你用文件名访 问其它模板。 你可以使用斜线访问子目录中的模板:

{% extends "layout/default.html" %}

这种行为也可能依赖于应用内嵌的 Jinja 。 注意子模板没有定义 footer 块,会 使用父模板中的值。

您不能在同一模板中定义多个具有相同名称的{% 阻止 %} 因为块标签以两种 方向工作,所以存在这种限制。 也就是说,块标记不仅仅提供一个占位符来填充 - 它还定义了填充中占位符的内容。 如果同一个模板中有两个同名的 {% templatetag openblock %} blok {% templatetag closeblock %} 标签,父模板 无法获知要使用哪一个块的内容。

如果你想要多次打印一个块,无论如何你可以使用特殊的 self 变量并调用与块同名 的函数:

<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}

超级块

可以调用 super 来渲染父级块的内容。 这会返回父级块的结果:

{% block sidebar %}
    <h3>Table Of Contents</h3>
    ...
    {{ super() }}
{% endblock %}

命名块结束标记

Jinja2 允许你在块的结束标签中加入的名称来改善可读性:

{% block sidebar %}
    {% block inner_sidebar %}
        ...
    {% endblock inner_sidebar %}
{% endblock sidebar %}

无论如何, endblock 后面的名称一定与块名匹配。

块嵌套和范围

嵌套块可以胜任更复杂的布局。 而默认的块不允许访问块外作用域中的变量:

{% for item in seq %}
    <li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}

这个例子会输出空的 <li> 项,因为 item 在块中是不可用的。 其原因是,如果 块被子模板替换,变量在其块中可能是未定义的或未被传递到上下文。

从 Jinja 2.2 开始,你可以显式地指定在块中可用的变量,只需在块声明中添加 scoped 修饰,就把块设定到作用域中:

{% for item in seq %}
    <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
{% endfor %}

当覆盖一个块时,不需要提供 scoped 修饰。

模板对象

在版本2.4中更改。

当一个模板对象被传递到模板上下文,你也可以从那个对象继承。 假设调用 代码传递 layout_template 布局模板到环境,这段代码会工作:

{% extends layout_template %}

之前 layout_template 变量一定是布局模板文件名的字符串才能工作。

HTML转义

当从模板生成 HTML 时,始终有这样的风险:变量包含影响已生成 HTML 的字符。 有两种方法:

  1. 有两种 解决方法:手动转义每个字符或默认自动转义所有的东西。
  2. 默认情况下自动转义所有内容。

Jinja支持两者。 Jinja 两者都支持,使用哪个取决于应用的配置。 默认的配置未开启自动转义有这样几个 原因:

  • 转义所有非安全值的东西也意味着 Jijna 转义已知不包含 HTML 的值,比如数字,对 性能有巨大影响。
  • 关于变量安全性的信息是易碎的。 可能会发生强制标记一个值为安全或非安全的情况, 而返回值会被作为 HTML 转义两次。

使用手动转义

如果启用了手动转义,按需转义变量就是 你的 责任。 要转义什么? If you have a variable that may include any of the following chars (>, <, &, or ") you SHOULD escape it unless the variable contains well-formed and trusted HTML. 转义通过|e过滤器管道变量:

{{ user.username|e }}

使用自动转义

当启用了自动转义,默认会转移一切,除非值被显式地标记为安全的。 变量和表达式可以标记为安全:

  1. 由应用程序使用MarkupSafe.Markup或上下文字典
  2. 模板,以及| safe过滤器

这种方法的主要问题是 Python 本 身没有被污染的值的概念,所以一个值是否安全的信息会丢失。

如果值未标记为安全,则将发生自动转义;这意味着你可能会得到双重转义的内容。但是,双转义很容易避免:只需依靠Jinja2提供的工具,不使用内置的Python结构,如str.format或字符串模运算符(%)

返回模板数据(宏、 superself.BLOCKNAME )的函数,其返回值总是被标记 为安全的。

具有自动转义的模板中的字符串文字被视为不安全,因为本机Python字符串(strunicodebasestring)不是MarkupSafe.Markup 具有__html__属性的字符串。

控制结构列表

控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/ekse )、 for 循环、以及宏和块之类的东西。 使用默认语法,控制结构出现在{% ... %}

For

遍历序列中的每项。 例如,要显示一个由 users` 变量提供的用户列表:

<h1>Members</h1>
<ul>
{% for user in users %}
  <li>{{ user.username|e }}</li>
{% endfor %}
</ul>

因为模板中的变量保留它们的对象属性,可以迭代像 dict 的容器:

<dl>
{% for key, value in my_dict.iteritems() %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

但请注意,Python字典没有排序;因此您可能要将tuple s或collections.OrderedDict的已排序list传递到模板,或使用 dictsort过滤器。

在一个 for 循环块中你可以访问这些特殊的变量:

变量 描述
loop.index 循环的当前迭代。(1索引)
loop.index0 循环的当前迭代。(0索引)
loop.revindex 到循环结束需要迭代的次数(从 1 开始)
loop.revindex0 到循环结束需要迭代的次数(从 0 开始)
loop.first 如果是第一次迭代,为 True 。
loop.last 如果是最后一次迭代,为 True 。
loop.length 序列中的项目数。
loop.cycle 在一串序列间期取值的辅助函数。 见下面的解释。
loop.depth指示渲染当前的递归循环的深度。 当前循环迭代的次数(从 1 开始)
loop.depth0指示渲染当前的递归循环的深度。 当前循环迭代的次数(从 0 开始)

在 for 循环中,可以使用特殊的 loop.cycle 辅助函数,伴随循环在一个字符串/变 量列表中周期取值:

{% for row in rows %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}

从 Jinja 2.1 开始,一个额外的 cycle 辅助函数允许循环限定外的周期取值。 更多信息请阅读 全局函数清单

与 Python 中不同,模板中的循环内不能 breakcontinue 但你可以在迭代 中过滤序列来跳过项目。 下面的例子中跳过了所有隐藏的用户:

{% for user in users if not user.hidden %}
    <li>{{ user.username|e }}</li>
{% endfor %}

好处是特殊的 loop 可以正确地计数,从而不计入未迭代过的用户。

如果因序列是空或者过滤移除了序列中的所有项目而没有执行循环,你可以使用 else 渲染一个用于替换的块:

<ul>
{% for user in users %}
    <li>{{ user.username|e }}</li>
{% else %}
    <li><em>no users found</em></li>
{% endfor %}
</ul>

注意,在Python中,只要对应的循环没有 breakelse由于Jinja循环不能打破,因此选择了else关键字稍有不同的行为。

也可以递归地使用循环。 当你处理诸如站点地图之类的递归数据时很有用。 要循环使用循环,您基本上必须将递归修饰符添加到循环定义中,并使用要迭代的新迭代方法调用循环变量。

下面的例子用递归循环实现了站点地图:

<ul class="sitemap">
{%- for item in sitemap recursive %}
    <li><a href="{{ item.href|e }}">{{ item.title }}</a>
    {%- if item.children -%}
        <ul class="submenu">{{ loop(item.children) }}</ul>
    {%- endif %}</li>
{%- endfor %}
</ul>

循环变量​​始终指最近(最内部)循环。如果我们有多个级别的循环,我们可以通过在我们想要递归使用的循环之后写入{%set outer_loop = loop%}来重新绑定变量循环然后,我们可以使用{{outer_loop(...)}}来调用它

If

Jinja 中的 if 语句可比 Python 中的 if 语句。 在最简单的形式中,你可以测试 一个变量是否未定义,为空或 false:

{% if users %}
<ul>
{% for user in users %}
    <li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}

对于多个分支,elifelse可以像Python中一样使用。 你也可以用更复杂的 表达式:

{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}

If 也可以被用作 内联表达式 并作为 循环过滤

宏类似常规编程语言中的函数。 它们用于把常用行为作为可重用的函数,取代 手动重复的工作。

这里是一个宏渲染表单元素的小例子:

{% macro input(name, value='', type='text', size=20) -%}
    <input type="{{ type }}" name="{{ name }}" value="{{
        value|e }}" size="{{ size }}">
{%- endmacro %}

在命名空间中,宏之后可以像函数一样调用:

<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

如果宏在不同的模板中定义,你需要首先使用 import

在宏内部,你可以访问三个特殊的变量:

varargs
如果有多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在 varargs 变量上。
kwargs
varargs ,但只针对关键字参数。 所有未使用的关键字参数会存储在 这个特殊变量中。
来电者
如果宏通过 call 标签调用,调用者会作为可调用的宏被存储在这个 变量中。

宏也可以暴露某些内部细节。 下面的宏对象属性是可用的:

名称
宏的名称。 {{ input.name }}将打印input
参数
一个宏接受的参数名的元组。
默认值
默认值的元组。
catch_kwargs
如果宏接受额外的关键字参数(也就是访问特殊的 kwargs 变量),为 true
catch_varargs
如果宏接受额外的位置参数(也就是访问特殊的 varargs 变量),为 true
来电者
如果宏访问特殊的 caller 变量且由 call 标签调用,为 true

如果一个宏的名称以下划线开始,它不是导出的且不能被导入。

调用

在某些情况下,需要把一个宏传递到另一个宏。 为此,可以使用特殊的 call 块。 下面的例子展示了如何让宏利用调用功能:

{% macro render_dialog(title, class='dialog') -%}
    <div class="{{ class }}">
        <h2>{{ title }}</h2>
        <div class="contents">
            {{ caller() }}
        </div>
    </div>
{%- endmacro %}

{% call render_dialog('Hello World') %}
    This is a simple dialog rendered by using a macro and
    a call block.
{% endcall %}

也可以向调用块传递参数。 这在为循环做替换时很有用。 总而言之,调用块的工作方 式几乎与宏相同,只是调用块没有名称。

这里是一个带参数的调用块的例子:

{% macro dump_users(users) -%}
    <ul>
    {%- for user in users %}
        <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
    {%- endfor %}
    </ul>
{%- endmacro %}

{% call(user) dump_users(list_of_user) %}
    <dl>
        <dl>Realname</dl>
        <dd>{{ user.realname|e }}</dd>
        <dl>Description</dl>
        <dd>{{ user.description }}</dd>
    </dl>
{% endcall %}

过滤器

过滤器段允许你在一块模板数据上应用常规 Jinja2 过滤器。 只需将代码包装在特殊的过滤器部分:

{% filter upper %}
    This text becomes uppercase
{% endfilter %}

作业

在代码块中,你也可以为变量赋值。 顶层的(块、宏、循环之外)赋值是可导出的,即 可以从别的模板中导入。

赋值使用 set 标签,并且可以为多个变量赋值:

{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}

块分配

版本2.8中的新功能。

从Jinja 2.8开始,还可以使用块分配来将块的内容捕获到变量名称中。这在一些情况下可以作为宏的替代方法是有用的。在这种情况下,不是使用等号和值,而是直接写入变量名,然后直到{% endset %} / t3>

例:

{% set navigation %}
    <li><a href="/">Index</a>
    <li><a href="/downloads">Downloads</a>
{% endset %}

导航变量​​包含导航HTML源代码。

扩展

extends 标签用于从另一个模板继承。 在文件中可以有多个扩展标签,但每次只能执行其中一个。

见上面的关于 模板继承 的节。

块用于继承,同时作为占位符和用于替换的内容。 模板继承 节中详细地介绍了块。

包括

include 语句用于包含一个模板,并在当前命名空间中返回那个文件的内容渲 染结果:

{% include 'header.html' %}
    Body
{% include 'footer.html' %}

被包含的模板默认可以访问活动的上下文中的变量。 更多关于导入和包含的上下文行为见 导入上下文行为

从 Jinja 2.2 开始,你可以把一句 include 用 ignore missing 标记,这样 如果模板不存在,Jinja 会忽略这条语句。 当与 withwithout context 语句联合使用时,它必须被放在上下文可见性语句 之前 这里是一些有效的例 子:

{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}

版本2.2中的新功能。

你也可以提供一个模板列表,它会在包含前被检查是否存在。 第一个存在的模板会 被包含进来。 如果给出了 ignore missing ,且所有这些模板都不存在,会退化 至不做任何渲染,否则将会抛出一个异常。

例:

{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}

Changed in version 2.4: 如果传递一个模板对象到模板上下文,你可以用 include 包含这个对 象。

导入

Jinja2 支持在宏中放置经常使用的代码。 这些宏可以被导入,并不同的模板中使用。 这 与 Python 中的 import 语句类似。 要知道的是,导入量会被缓存,并且默认下导入的 模板不能访问当前模板中的非全局变量。 更多关于导入和包含的上下文 行为见 导入上下文行为

有两种方式来导入模板。 你可以把整个模板导入到一个变量或从其中导入请求特定的宏 /导出量。

比如我们有一个渲染表单(名为 forms.html )的助手模块:

{% macro input(name, value='', type='text') -%}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}

{%- macro textarea(name, value='', rows=10, cols=40) -%}
    <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
        }}">{{ value|e }}</textarea>
{%- endmacro %}

最简单灵活的方式是把整个模块导入为一个变量。 这样你可以访问属性:

{% import 'forms.html' as forms %}
<dl>
    <dt>Username</dt>
    <dd>{{ forms.input('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>

此外你也可以从模板中导入名称到当前的命名空间:

{% from 'forms.html' import input as input_field, textarea %}
<dl>
    <dt>Username</dt>
    <dd>{{ input_field('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ input_field('password', type='password') }}</dd>
</dl>
<p>{{ textarea('comment') }}</p>

名称以一个或更多下划线开始的宏和变量是私有的,不能被导入。

Changed in version 2.4: 如果传递一个模板对象到模板上下文,从那个对象中导入。

导入上下文行为

默认下,每个包含的模板会被传递到当前上下文,而导入的模板不会。 这样做的原因 是导入量不会像包含量被缓存,因为导入量经常只作容纳宏的模块。

通过在 import/include 声明中直接添加 with contextwithout context ,当前的上下文可以传递到模板,而且不会 自动禁用缓存。

这里有两个例子:

{% from 'forms.html' import input with context %}
{% include 'header.html' without context %}

注意

在 Jinja 2.0 中,被传递到被包含模板的上下文不包含模板中定义的变量。 事实上,这不能工作:

{% for box in boxes %}
    {% include "render_box.html" %}
{% endfor %}

在 Jinja 2.0 中,被包含的模板 render_box.html 不能 访问 box 从 Jinja 2.1 开始, render_box.html 可以 这么做。

表达式

Jinja 中到处都允许使用基本表达式。 这像常规的 Python 一样工作,即使你不用 Python 工作,你也会感受到其带来的便利。

字面值

表达式最简单的形式就是字面量。 字面量表示诸如字符串和数值的 Python 对象。 存在以下文字:

“你好,世界”:
双引号或单引号中间的一切都是字符串。 无论何时你需要在模板中使用一个字 符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),它们都是 有用的。
42 / 42.23:
直接写下数值就可以创建整数和浮点数。 如果有小数点,则为浮点数,否则为 整数。 请记住,在Python中,4242.0是不同的(分别是intfloat)。
['list','of','objects']:

一对中括号括起来的东西是一个列表。 列表用于存储和迭代序列化的数据。 例如 你可以容易地在 for 循环中用列表和元组创建一个链接的列表:

<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
                         ('downloads.html', 'Downloads')] %}
    <li><a href="{{ href }}">{{ caption }}</a></li>
{% endfor %}
</ul>
('tuple','of','values'):
元组与列表类似,只是你不能修改元组。 如果一个元组只有一个项目,它必须后跟一个逗号(('1-tuple',))。 元组通常用于表示两个或更多元素的项。 更多细节见上面的例子。
{'dict':'of','key':'和','value':'pairs'}:
Python 中的字典是一种关联键和值的结构。 键必须是唯一的,并且键必须只有一个 值。 字典在模板中很少使用,罕用于诸如 xmlattr() 过滤器之类。
<
true 永远是 true ,而 false 始终是 false 。

注意

特殊常量 truefalsenone 实际上是小写的。 因为过去造成混乱(True用于展开为未定义的变量,被认为是false),所有三个现在也可以用标题大小写(True FalseNone)。 尽管如此, 为了一致性(所有的 Jinja 标识符是小写的),你应该使用小写的版本。

数学

Jinja 允许你用计算值。 这在模板中很少用到,但是为了完整性允许其存在。 支持下面的 运算符:

+
把两个对象加到一起。 这个语句通常是用来操作数字的,但是如果你想连接两个字符串或者两个列表,也是可以的。但是,如果你想连接两个字符串的话,这并不是一个好的方式! 连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2.
-
用第一个数减去第二个数。 {{ 3 - 2 }} 等于 1.
/
对两个数做除法。 返回值会是一个浮点数。 {{ 1 / 2 }} 等于 {{ 0.5 }}.(就像from __ future __ import division。)
//
对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2.
%
计算整数除法的余数。 {{ 11 7 }} 4
*
用右边的数乘左边的操作数。 {{ 2 * 2 }} 返回4 也可以用于重 复一个字符串多次。 {'foo' : 'bar'}dict(foo=bar) 等价。
**
取左操作数的右操作数次幂。 {{ 2 ** 3 }}将返回8

比较

==
比较两个对象是否相等。
!=
比较两个对象是否不等。
>
如果左边大于右边,返回 true
>=
如果左边大于等于右边,返回 true
如果左边小于右边,返回 true
<=
如果左边小于等于右边,返回 true

逻辑

对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式:

如果左操作数和右操作数同为真,返回 true 。
要么
如果左操作数和右操作数有一个为真,返回 true 。
对一个表达式取反(见下)。
(expr)
表达式组。

注意

The is and in operators support negation using an infix notation, too: foo is not bar and foo not in bar instead of not foo is bar and not foo in bar. 所有的 其它表达式需要前缀记法 not (foo and bar)

其他运算符

下面的运算符非常有用,但不适用于其它的两个分类:

运行序列/映射包含检查。 如果左操作数包含于右操作数,返回 true 。 {{ 1 in [1, 2, 3] }} would, for example, return true.
运行一个 测试
|
应用一个 过滤器
~

把所有的操作数转换为字符串,并且连接它们。

{{ "Hello " ~ name ~ "!" }} would return (assuming name is set to 'John') Hello John!.

()

调用一个可调用量:{% templatetag openvariable %} post.render() {% templatetag closevariable %}在括号内部,您可以使用位置参数和关键字参数,如Python:

{{ post.render(user, full = true) }}

. . / []
获取一个对象的属性。 (见 变量

If 表达式

同样,也可以使用内联的 if 表达式。 这在某些情况很有用。 例如你可以用来在一个 变量定义的情况下才继承一个模板,否则继承默认的布局模板:

{% extends layout_template if layout_template is defined else 'master.html' %}

一般的语法是 <do something> if <something is true> else <do something else>

else 部分是可选的。 如果没有显式地提供 else 块,会求值一个未定义对象:

{{ '[%s]' % page.title if page.title }}

List of Builtin Filters

abs(number)

返回参数的绝对值。

attr(obj, name)

获取对象的属性。foo|attr("bar")的工作方式类似于foo.bar,只是总是返回一个属性,并且不查找项。

有关详细信息,请参阅Notes on subscriptions

batch(value, linecount, fill_with=None)

批量项目的过滤器。它的工作方式非常类似于切片刚好相反。它返回具有给定数量项目的列表的列表。如果您提供第二个参数,则用于填充缺少的项目。 例子:

<table>
{%- for row in items|batch(3, '&nbsp;') %}
  <tr>
  {%- for column in row %}
    <td>{{ column }}</td>
  {%- endfor %}
  </tr>
{%- endfor %}
</table>
capitalize(s)

大写一个值。第一个字符将为大写,所有其他为小写。

center(value, width=80)

将值置于给定宽度的字段中。

default(value, default_value='', boolean=False)

如果值未定义,它将返回传递的默认值,否则变量的值:

{{ my_variable|default('my_variable is not defined') }}

This will output the value of my_variable if the variable was defined, otherwise 'my_variable is not defined'. 如果要对变量计算为false的变量使用默认值,则必须将第二个参数设置为true

{{ ''|default('the string was empty', true) }}
别名: d
dictsort(value, case_sensitive=False, by='key')

排序dict和yield(键,值)对。因为python dicts是未排序的,您可能想使用此函数按键或值对它们排序:

{% for item in mydict|dictsort %}
    sort the dict by key, case insensitive

{% for item in mydict|dictsort(true) %}
    sort the dict by key, case sensitive

{% for item in mydict|dictsort(false, 'value') %}
    sort the dict by value, case insensitive
escape(s)

将字符串s中的字符&,,'和“转换为HTML安全序列。如果您需要显示可能在HTML中包含此类字符的文本,请使用此选项。标记返回值作为标记字符串。

别名: e
filesizeformat(value, binary=False)

将该值格式化为“人类可读”文件大小(即13 kB,4.1 MB,102字节等)。使用每个默认十进制前缀(Mega,Giga等),如果第二个参数设置为True,则使用二进制前缀(Mebi,Gibi)。

first(seq)

返回序列的第一项。

float(value, default=0.0)

None如果转换不起作用,它将返回0.0您可以使用第一个参数覆盖此默认值。

forceescape(value)

强制执行HTML转义。这可能会双重转义变量。

format(value, *args, **kwargs)

Navigation

{{ "%s - %s"|format("Hello?", "Foo!") }}
    -> Hello? - Foo!
groupby(value, attribute)

通过公共属性对对象序列进行分组。

例如,如果您有代表性别first_namelast_name属性的人的列表或对象,并且您要将所有用户分组性别,你可以做类似下面的代码片段:

<ul>
{% for group in persons|groupby('gender') %}
    <li>{{ group.grouper }}<ul>
    {% for person in group.list %}
        <li>{{ person.first_name }} {{ person.last_name }}</li>
    {% endfor %}</ul></li>
{% endfor %}
</ul>

此外,它可以使用元组解包为grouper和列表:

<ul>
{% for grouper, list in persons|groupby('gender') %}
    ...
{% endfor %}
</ul>

您可以看到我们分组的项目存储在grouper属性中,而列表包含具有此分组器的所有对象。

在版本2.6中更改:现在可以使用点分表法按另一个属性的子属性分组。

indent(s, width=4, indentfirst=False)

返回一个传递的字符串的副本,每行缩进4个空格。第一行不缩进。如果你想改变空格数或缩进第一行,你可以传递额外的参数到过滤器:

{{ mytext|indent(2, true) }}
    indent by two spaces and indent the first line too.
int(value, default=0, base=10)

将值转换为整数。如果转换不起作用,它将返回0您可以使用第一个参数覆盖此默认值。您还可以覆盖第二个参数中的默认基址(10),它分别为基址2,8和16处理带有前缀的输入,例如0b,0o和0x。

join(value, d='', attribute=None)

连接器接受一个字符串,每次被调用时返回 那个字符串,除了第一次调用时返回一个空字符串。 默认情况下,元素之间的分隔符是空字符串,您可以使用可选参数定义它:

{{ [1, 2, 3]|join('|') }}
    -> 1|2|3

{{ [1, 2, 3]|join }}
    -> 123

也可以连接对象的某些属性:

{{ users|join(', ', attribute='username') }}

版本2.6中的新功能:添加了属性参数。

last(seq)

返回序列的最后一项。

length(object)

返回序列或集合的项目数。

别名:count
list(value)

将值转换为列表。如果它是一个字符串,返回的列表将是一个字符列表。

lower(s)

将值转换为小写。

map()

在对象序列上应用过滤器或查找属性。这在处理对象列表时很有用,但是你真的只对它的某个值感兴趣。

基本用法是对属性进行映射。假设您有一个用户列表,但您只对用户名列表感兴趣:

Users on this page: {{ users|map(attribute='username')|join(', ') }}

或者,您可以让它通过传递过滤器的名称和参数调用过滤器。一个好的例子是在序列上应用文本转换过滤器:

Users on this page: {{ titles|map('lower')|join(', ') }}

版本2.7中的新功能。

pprint(value, verbose=False)

漂亮打印一个变量。用于调试。

你可以使用它来连接: 如果这个参数是真的,输出将更详细(这需要漂亮

random(seq)

从序列返回一个随机项目。

reject()

通过对对象应用测试来过滤对象序列,并拒绝测试成功的对象。

用法示例:

{{ numbers|reject("odd") }}

版本2.7中的新功能。

rejectattr()

通过对对象的属性或属性应用测试来过滤对象序列,并拒绝测试成功的那些对象。

{{ users|rejectattr("is_active") }}
{{ users|rejectattr("email", "none") }}

版本2.7中的新功能。

replace(s, old, new, count=None)

返回值的副本,将所有出现的子串替换为新的。第一个参数是应该替换的子字符串,第二个是替换字符串。如果给出了可选的第三个参数count,则只替换第一个count

{{ "Hello World"|replace("Hello", "Goodbye") }}
    -> Goodbye World

{{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
    -> d'oh, d'oh, aaargh
reverse(value)

反转对象或返回一个迭代器,它以其他方式迭代。

round(value, precision=0, method='common')

将数字四舍五入为给定的精度。第一个参数指定精度(默认为0),第二个舍入方法:

  • 'common'向上或向下舍入
  • 'ceil'总是向上舍入
  • 'floor'总是向下舍入

如果未指定方法'common'

{{ 42.55|round }}
    -> 43.0
{{ 42.55|round(1, 'floor') }}
    -> 42.5

请注意,即使舍入为0精度,也会返回float。如果你需要一个实整数,通过int

{{ 42.55|round|int }}
    -> 43
safe(value)

将该值标记为安全,这意味着在启用自动转义的环境中,此变量不会被转义。

select()

None

用法示例:

{{ numbers|select("odd") }}
{{ numbers|select("odd") }}

版本2.7中的新功能。

selectattr()

通过对对象的属性应用测试来筛选对象序列,并且仅选择测试成功的那些。

用法示例:

{{ users|selectattr("is_active") }}
{{ users|selectattr("email", "none") }}

版本2.7中的新功能。

slice(value, slices, fill_with=None)

切片迭代器并返回包含这些项的列表列表。如果您想创建一个包含三个表示列的标签的div:

<div class="columwrapper">
  {%- for column in items|slice(3) %}
    <ul class="column-{{ loop.index }}">
    {%- for item in column %}
      <li>{{ item }}</li>
    {%- endfor %}
    </ul>
  {%- endfor %}
</div>

如果你传递第二个参数,它用于填充最后一次迭代的缺失值。

sort(value, reverse=False, case_sensitive=False, attribute=None)

排序迭代。每个默认它排​​序升序,如果你传递true作为第一个参数,它会反转排序。

如果迭代由字符串组成,则第三个参数可用于控制默认情况下禁用的比较的大小写敏感性。

{% for item in iterable|sort %}
    ...
{% endfor %}

还可以通过指定属性参数按属性(例如,按对象的日期排序)进行排序:

{% for item in iterable|sort(attribute='date') %}
    ...
{% endfor %}

在版本2.6中已更改:添加了属性参数。

string(s)

如果字符串unicode不是已经是一个字符串。这样一个标记字符串不会转换回unicode。

striptags(value)

剥离SGML / XML标签,并用一个空格替换相邻的空格。

sum(iterable, attribute=None, start=0)

)默认为 0 。 当序列为空时,它返回开始。

也可以只总结某些属性:

Total: {{ items|sum(attribute='price') }}

在版本2.6中已更改:添加了属性参数以允许对属性进行汇总。此外,将start参数移到右侧。

title(s)

Convert a value to lowercase. 也就是说单词将以大写字母开头,所有剩余的字符都是小写。

trim(value)

剥离前导和尾随空格。

truncate(s, length=255, killwords=False, end='...')

返回字符串的截断副本。长度使用默认为255的第一个参数指定。如果第二个参数为true,过滤器将剪切文本的长度。否则将丢弃最后一个字。如果文本实际上被截断,它将附加一个省略号("...")。如果您想使用与"..."不同的省略号,您可以使用第三个参数指定。

{{ "foo bar baz"|truncate(9) }}
    -> "foo ..."
{{ "foo bar baz"|truncate(9, True) }}
    -> "foo ba..."
upper(s)

将值转换为大写。

urlencode(value)

转义字符串以在URL中使用(使用UTF-8编码)。它接受字典和常规字符串以及成对迭代。

版本2.7中的新功能。

urlize(value, trim_url_limit=None, nofollow=False, target=None)

将纯文本的网址转换为可点击的链接。

如果你通过过滤器一个额外的整数,它会缩短到该数字的url。还有第三个参数存在,使urls“nofollow”:

{{ mytext|urlize(40, true) }}
    links are shortened to 40 chars and defined with rel="nofollow"

如果指定target,则target属性将添加到<a>

{{ mytext|urlize(40, target='_blank') }}

在版本2.8+中更改:添加了目标参数。

wordcount(s)

计算字符串中的单词。

wordwrap(s, width=79, break_long_words=True, wrapstring=None)

返回传递到79字符后包裹的过滤器的字符串的副本。您可以使用第一个参数覆盖此默认值。如果您将第二个参数设置为false如果Jinja不长于width,则不会拆分字。默认情况下,换行符将是环境的默认换行符,但这可以使用wrapstring关键字参数更改。

New in version 2.7: Added support for the wrapstring parameter.

xmlattr(d, autospace=True)

基于dict中的项目创建SGML / XML属性字符串。所有既不是none也不是未定义的值都会自动转义:

<ul{{ {'class': 'my_list', 'missing': none,
        'id': 'list-%d'|format(variable)}|xmlattr }}>
...
</ul>

结果像这样:

<ul class="my_list" id="list-42">
...
</ul>

正如你所看到的,如果过滤器返回了一些东西,除非第二个参数为false,否则它会自动在项前加上一个空格。

List of Builtin Tests

callable(object)

返回对象是否可调用(即某种函数)。注意,类是可调用的,类的实例也是用__call __()方法。

defined(value)

true / false:

{% if variable is defined %}
    value of variable: {{ variable }}
{% else %}
    variable is not defined
{% endif %}

有关设置未定义变量的简单方法,请参阅default()过滤器。

divisibleby(value, num)

检查变量是否可被数字整除。

equalto(value, other)

检查对象是否与另一个对象具有相同的值:

{% if foo.expression is equalto 42 %}
    the foo attribute evaluates to the constant 42
{% endif %}

这似乎是一个无用的测试,因为它与==运算符完全相同,但与selectattr函数一起使用时可能很有用:

{{ users|selectattr("email", "equalto", "[email protected]") }}

版本2.8中的新功能。

escaped(value)

检查值是否转义。

even(value)

如果变量是偶数,则返回true。

iterable(value)

检查是否可以遍历一个对象。

如果变量为lowercased,则返回true。

mapping(value)

如果对象是映射(dict等),则返回true)。

版本2.6中的新功能。

none(value)

Return true if the variable is none.

number(value)

如果变量是数字,则返回true。

odd(value)

如果变量是奇数,则返回true。

sameas(value, other)

检查对象是否指向与另一个对象相同的内存地址:

{% if foo.attribute is sameas false %}
    the foo attribute really is the `False` singleton
{% endif %}
sequence(value)

如果变量是序列,则返回true。序列是可迭代的变量。

string value

如果对象是字符串,则返回true。

undefined(value)

Like defined() but the other way round.

如果变量为大写,则返回true。

List of Global Functions

默认下,下面的函数在全局作用域中可用:

range([start, ]stop[, step])

返回一个包含整等差级数的列表。 范围(i, j)返回[i, i + t5> i + 2, ..., j-1]开始(!)默认为0 当给定了公差,它决定了增长(或减小)。 例如,range(4)范围(0, 4, 1) t2 > return [0, 1, 2, 3]终点被省略! 这些是一个 4 元素 数组的有效索引值。

例如重复一个模板块多次来填充一个列表是有用的。 想向你有一个 7 个用户的 列表,但你想要渲染三个空项目来用 CSS 强制指定高度:

<ul>
{% for user in users %}
    <li>{{ user.username }}</li>
{% endfor %}
{% for number in range(10 - users|count) %}
    <li class="empty"><span>...</span></li>
{% endfor %}
</ul>
lipsum(n=5, html=True, min=20, max=100)

在模板中生成 lorem ipsum 乱数假文。 默认会生成 5 段 HTML ,每段在 20 到 100 词之间。 如果 HTML 被禁用,会返回常规文本。 这在测试布局时生成简单内容时很有 用。

dict(**items)

一个方便的替代字面量。{'foo': 'bar'}dict(foo='bar')相同。

class cycler(*items)

周期计允许你在若干个值中循环,类似 loop.cycle 的工作方式。 不同于 loop.cycle 的是,无论如何你都可以在循环外或在多重循环中使用它。

比如如果你想要显示一个文件夹和文件列表,且文件夹在上,它们在同一个列表中且 行颜色是交替的。

以下示例说明如何使用cycler

{% set row_class = cycler('odd', 'even') %}
<ul class="browser">
{% for folder in folders %}
  <li class="folder {{ row_class.next() }}">{{ folder|e }}</li>
{% endfor %}
{% for filename in files %}
  <li class="file {{ row_class.next() }}">{{ filename|e }}</li>
{% endfor %}
</ul>

循环仪具有以下属性和方法:

reset()

重置周期计到第一个项。

next()

返回当前项并跳转到下一个。

current

返回当前项。

new in Jinja 2.1

class joiner(sep=', ')

一个小助手,可用于“连接”多个部分。joiner被传递一个字符串,并且每次被调用时返回该字符串,除了第一次(在这种情况下,它返回一个空字符串)。你可以使用它来加入事物:

{% set pipe = joiner("|") %}
{% if categories %} {{ pipe() }}
    Categories: {{ categories|join(", ") }}
{% endif %}
{% if author %} {{ pipe() }}
    Author: {{ author() }}
{% endif %}
{% if can_edit %} {{ pipe() }}
    <a href="?action=edit">Edit</a>
{% endif %}

new in Jinja 2.1

Extensions

下面的几节涵盖了可能被应用启用的 Jinja2 内置的扩展。 应用程序还可以提供本文档未涵盖的进一步扩展;在这种情况下应该有解释所述extensions的单独文档。

i18n

如果启用来 i18n 扩展,可以把模板中的部分标记为可译的。 标记一个段为可译的,可 以使用 trans:

<p>{% trans %}Hello {{ user }}!{% endtrans %}</p>

要翻译一个模板表达式——比如使用模板过滤器或访问对象的属性——你需要绑定表达式到 一个名称来在翻译块中使用:

<p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p>

如果你需要在 trans 标签中绑定一个以上的表达式,用逗号来分割( , ):

{% trans book_title=book.title, author=author.name %}
This is {{ book_title }} by {{ author }}
{% endtrans %}

在翻译块中不允许使用语句,只能使用变量标签。

为表示复数,在 transendtrans 之间用 pluralize 标签同时指定单数和复 数形式:

{% trans count=list|length %}
There is {{ count }} {{ name }} object.
{% pluralize %}
There are {{ count }} {{ name }} objects.
{% endtrans %}

默认情况下块中的第一个变量用于决定使用单数还是复数。 如果这不奏效,你可以指定 用于复数的名称作为 pluralize 的参数:

{% trans ..., user_count=users|length %}...
{% pluralize user_count %}...{% endtrans %}

也可以翻译表达式中的字符串。 为此,有三个函数:

  • gettext:翻译单个字符串
  • caller
  • _gettext的别名

例如你可以容易地这样打印一个已翻译的字符串:

{{ _('Hello World!') }}

要使用占位符,请使用格式过滤器:

{{ _('Hello %(user)s!')|format(user=user.username) }}

对于多个占位符,请始终对格式使用关键字参数,因为其他语言可能不会使用相同顺序的单词。

在版本2.5中更改。

如果激活了新样式的 gettext 调用( 新样式 Gettext ),使用占位符 会更加简单:

{{ gettext('Hello World!') }}
{{ gettext('Hello %(name)s!', name='World') }}
{{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }}

注意 ngettext 函数的格式化字符串自动接受 num 参数作为计数作为附加的 常规参数。

Expression Statement

如果加载了表达式语句扩展,则可以使用名为do的标签,其工作方式与常规变量表达式完全一样({{ ... }});除了它不打印任何东西。 这可以用于修改列表:

{% do navigation.append('a string') %}

Loop Controls

如果应用启用来 循环控制 ,则可以在循环中使用 breakcontinue当达到break时,循环终止;如果达到continue,则停止处理并继续下一次迭代。

这个循环每两项跳过一次:

{% for user in users %}
    {%- if loop.index is even %}{% continue %}{% endif %}
    ...
{% endfor %}

同样,这个循环 10 次迭代之后会终止处理:

{% for user in users %}
    {%- if loop.index >= 10 %}{% break %}{% endif %}
{%- endfor %}

请注意,loop.index以1开头,而loop.index0以0开头(请参阅:For)。

With 语句

版本2.3中的新功能。

如果应用启用了 With 语句 ,将允许在模板中使用 with 关键 字。 这使得创建一个新的内作用域。 这个作用域中的变量在外部是不可见的。

简而言之:

{% with %}
    {% set foo = 42 %}
    {{ foo }}           foo is 42 here
{% endwith %}
foo is not visible here any longer

因为通常在范围开头设置变量,所以可以在语句中进行。 下面的两 个例子是等价的:

{% with foo = 42 %}
    {{ foo }}
{% endwith %}

{% with %}
    {% set foo = 42 %}
    {{ foo }}
{% endwith %}

Autoescape Extension

版本2.4中的新功能。

如果你的应用程序设置了 自动转义扩展 ,你就可以在模版中开启或者关闭自动转义。

例:

{% autoescape true %}
    Autoescaping is active within this block
{% endautoescape %}

{% autoescape false %}
    Autoescaping is inactive within this block
{% endautoescape %}

endautoescape 标签之后,自动转义的行为将回到与之前相同的状态。