模板设计器文档 — Jinja 文档

来自菜鸟教程
Jinja/docs/3.0.x/templates
跳转至:导航、​搜索

模板设计器文档

本文档描述了模板引擎的语法和语义,对于创建 Jinja 模板的人来说将是最有用的参考。 由于模板引擎非常灵活,应用程序的配置可能与此处介绍的代码在未定义值的分隔符和行为方面略有不同。

概要

Jinja 模板只是一个文本文件。 Jinja 可以生成任何基于文本的格式(HTML、XML、CSV、LaTeX 等)。 Jinja 模板不需要有特定的扩展名:.html.xml 或任何其他扩展名就可以了。

模板包含 变量 和/或 表达式 ,当模板被 渲染 时,它们会被替换为值; 和 tags,控制模板的逻辑。 模板语法深受 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 }}

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

以下示例显示了默认配置设置。 应用程序开发人员可以将语法配置从 {% foo %} 更改为 <% foo %> 或类似内容。

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

行语句和注释 也是可能的,尽管它们没有默认的前缀字符。 要使用它们,请在创建 环境 时设置 line_statement_prefixline_comment_prefix

模板文件扩展名

如上所述,任何文件都可以作为模板加载,无论文件扩展名如何。 添加 .jinja 扩展,如 user.html.jinja 可能会使某些 IDE 或编辑器插件更容易,但不是必需的。 稍后介绍的自动转义可以根据文件扩展名应用,因此在这种情况下您需要考虑额外的后缀。

识别模板的另一个很好的启发是它们位于 templates 文件夹中,无论扩展名如何。 这是项目的常见布局。


变量

模板变量由传递给模板的上下文字典定义。

只要应用程序传入模板中的变量,您就可以处理它们。 变量可能具有您也可以访问的属性或元素。 变量具有哪些属性在很大程度上取决于提供该变量的应用程序。

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

以下几行做同样的事情:

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

重要的是要知道外部双花括号是变量的 不是 部分,而是打印语句。 如果您访问标签内的变量,请不要在它们周围放置大括号。

如果变量或属性不存在,您将返回一个未定义的值。 您可以用这种值做什么取决于应用程序配置:默认行为是在打印或迭代时评估为空字符串,并且对其他所有操作都失败。

执行

为了方便,Jinja中的foo.bar在Python层做了以下事情:

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

foo['bar'] 的工作原理大致相同,但顺序略有不同:

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

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


过滤器

可以通过 过滤器 修改变量。 过滤器通过管道符号 (|) 与变量分开,并且可以在括号中包含可选参数。 可以链接多个过滤器。 一个过滤器的输出应用于下一个。

例如,模板:Name 将从变量 name 中删除所有 HTML 标签,并将输出 (title(striptags(name))) 设为标题。

接受参数的过滤器在参数周围有括号,就像函数调用一样。 例如:模板:Listx 将加入一个带逗号的列表 (str.join(', ', listx))。

下面的 内置过滤器列表 描述了所有内置过滤器。


测试

除了过滤器,还有所谓的“测试”可用。 测试可用于针对公共表达式测试变量。 要测试变量或表达式,请在变量后添加 is 加上测试名称。 例如,要确定变量是否已定义,您可以执行 name is defined,然后根据 name 是否在当前模板上下文中定义,返回 true 或 false。

测试也可以接受参数。 如果测试只采用一个参数,则可以省略括号。 例如,以下两个表达式做同样的事情:

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

下面的 内置测试列表 描述了所有内置测试。


评论

要注释掉模板中的部分行,请使用默认设置为 {# ... #} 的注释语法。 这对于注释掉用于调试的模板部分或为其他模板设计者或您自己添加信息非常有用:

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

空白控制

在默认配置中:

  • 如果存在,则删除单个尾随换行符
  • 其他空白(空格、制表符、换行符等)原样返回

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

启用 trim_blocks 和 lstrip_blocks 后,您可以将块标记放在自己的行上,渲染时将删除整个块行,保留内容的空白。 例如,如果没有 trim_blocks 和 lstrip_blocks 选项,这个模板:

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

在 div 中用空行呈现:

<div>

        yay

</div>

但同时启用 trim_blocks 和 lstrip_blocks 时,模板块行被删除并保留其他空白:

<div>
        yay
</div>

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

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

同样,您可以通过在块的末尾放置一个加号 (+) 来手动禁用 trim_blocks 行为:

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

您还可以手动去除模板中的空白。 如果在块的开头或结尾添加减号 (-)(例如 For 标签)、注释或变量表达式,该块之前或之后的空格将被删除:

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

这将产生它们之间没有空格的所有元素。 如果 seq 是从 19 的数字列表,输出将是 123456789

如果启用 行语句 ,它们会自动去除前导空格直到行首。

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

笔记

不得在标记和减号之间添加空格。

有效

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

无效

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

逃跑

有时需要——甚至是必要的——让 Jinja 忽略原本会作为变量或块处理的部分。 例如,如果使用默认语法,您想使用 {{ 作为模板中的原始字符串而不是启动变量,则必须使用一个技巧。

输出文字变量分隔符 ({{) 的最简单方法是使用变量表达式:

{{ '{{' }}

对于较大的部分,标记一个块 raw 是有意义的。 例如,要在模板中包含示例 Jinja 语法,您可以使用以下代码段:

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

笔记

{% raw -%} 标签末尾的减号清除原始数据第一个字符之前的所有空格和换行符。


行语句

如果应用程序启用了 line 语句,则可以将 line 标记为语句。 例如,如果行语句前缀配置为 #,则以下两个示例是等效的:

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

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

行语句前缀可以出现在行上的任何位置,只要它前面没有文本即可。 为了更好的可读性,开始一个块的语句(例如 for、if、elif 等)可能以冒号结尾:

# 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 标签所做的就是告诉模板引擎子模板可能会覆盖模板中的那些占位符。

block 标签可以在其他块中,例如 if,但无论 if 块是否被实际渲染,它们都会被执行。


子模板

子模板可能如下所示:

{% 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 %} 标签是这里的关键。 它告诉模板引擎这个模板“扩展”了另一个模板。 当模板系统评估这个模板时,它首先定位父模板。 extends 标签应该是模板中的第一个标签。 之前的一切都正常打印出来,可能会造成混乱。 有关此行为以及如何利用它的详细信息,请参阅 Null-Default Fallback。 此外,无论周围条件被评估为真还是假,块都将始终被填充。

模板的文件名取决于模板加载器。 例如,FileSystemLoader 允许您通过提供文件名来访问其他模板。 您可以使用斜杠访问子目录中的模板:

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

但是这种行为可能取决于嵌入 Jinja 的应用程序。 请注意,由于子模板未定义 footer 块,因此使用来自父模板的值。

您不能在同一个模板中定义多个具有相同名称的 {% block %} 标签。 存在此限制是因为块标记在“双向”方向上都起作用。 也就是说,块标记不仅提供要填充的占位符 - 它还定义了填充 中占位符的内容。 如果模板中有两个名称相似的 {% block %} 标签,则该模板的父级将不知道要使用哪个块的内容。

但是,如果要多次打印块,则可以使用特殊的 self 变量并使用该名称调用块:

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

超级积木

可以通过调用 super() 来渲染父块的内容。 这将返回父块的结果:

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

嵌套扩展

在多级 {% extends %} 的情况下,可以链接 super 引用(如 super.super())以跳过继承树中的级别。

例如:

# parent.tmpl
body: {% block body %}Hi from parent.{% endblock %}

# child.tmpl
{% extends "parent.tmpl" %}
{% block body %}Hi from child. {{ super() }}{% endblock %}

# grandchild1.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild1.{% endblock %}

# grandchild2.tmpl
{% extends "child.tmpl" %}
{% block body %}Hi from grandchild2. {{ super.super() }} {% endblock %}

渲染 child.tmpl 将得到 body: Hi from child. Hi from parent.

渲染 grandchild1.tmpl 将得到 body: Hi from grandchild1.

渲染 grandchild2.tmpl 将得到 body: Hi from grandchild2. Hi from parent.


命名块结束标签

Jinja 允许您将块的名称放在结束标记之后以获得更好的可读性:

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

但是,endblock 字后的名称必须与块名称匹配。


块嵌套和作用域

块可以嵌套用于更复杂的布局。 但是,默认块可能无法从外部作用域访问变量:

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

此示例将输出空的 <li> 项,因为 项 在块内不可用。 这样做的原因是,如果块被子模板替换,则会出现一个未在块中定义或传递给上下文的变量。

从 Jinja 2.2 开始,您可以通过将 scoped 修饰符添加到块声明来将块设置为“作用域”,从而明确指定块中的变量可用:

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

覆盖块时,不必提供 scoped 修饰符。


所需块

块可以标记为 required。 它们必须在某个时候被覆盖,但不一定被直接子模板覆盖。 必填块只能包含空格和注释,不能直接渲染。

page.txt

{% block body required %}{% endblock %}

issue.txt

{% extends "page.txt" %}

bug_report.txt

{% extends "issue.txt" %}
{% block body %}Provide steps to demonstrate the bug.{% endblock %}

渲染 page.txtissue.txt 将引发 TemplateRuntimeError,因为它们不会覆盖 body 块。 渲染 bug_report.txt 会成功,因为它确实覆盖了块。

当与 scoped 结合使用时,required 修饰符必须放在 作用域修饰符之后。 下面是一些有效的例子:

{% block body scoped %}{% endblock %}
{% block body required %}{% endblock %}
{% block body scoped required %}{% endblock %}

模板对象

extendsincludeimport 可以使用模板对象而不是模板名称来加载。 这在某些高级情况下可能很有用,因为您可以使用 Python 代码首先加载模板并将其传递给 render

if debug_mode:
    layout = env.get_template("debug_layout.html")
else:
    layout = env.get_template("layout.html")

user_detail = env.get_template("user/detail.html", layout=layout)
{% extends layout %}

请注意 extends 如何通过传递给 render 的模板对象而不是字符串传递变量。


HTML 转义

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

  1. 手动转义每个变量; 或者
  2. 默认情况下自动转义所有内容。

Jinja 支持两者。 使用什么取决于应用程序配置。 默认配置为不自动转义; 出于各种原因:

  • 转义除安全值之外的所有内容也意味着 Jinja 正在转义已知不包含 HTML 的变量(例如 数字,布尔值)这可能是一个巨大的性能损失。
  • 有关变量安全性的信息非常脆弱。 有可能通过强制安全和不安全值,返回值是双重转义的 HTML。

使用手动转义

如果启用手动转义,则 您的 负责在需要时转义变量。 逃避什么? 如果您的变量 可能 包含以下任何字符(><&"应该 对其进行转义,除非该变量包含格式正确且受信任的 HTML。 转义的工作原理是通过 |e 过滤器管道变量:

{{ user.username|e }}

使用自动转义

启用自动转义后,除了明确标记为安全的值外,默认情况下所有内容都会被转义。 变量和表达式可以在以下任一情况下标记为安全:

  1. 应用程序的上下文字典 markupsafe.Markup
  2. 带有 |safe 过滤器的模板。

如果您标记为安全的字符串通过其他不理解该标记的 Python 代码传递,它可能会丢失。 在到达模板之前,请注意您的数据何时被标记为安全以及它是如何处理的。

如果一个值已经被转义但没有被标记为安全,自动转义仍然会发生并导致双重转义字符。 如果您知道您的数据已经安全但未标记,请务必将其包裹在 Markup 中或使用 |safe 过滤器。

Jinja 函数(宏、super、self.BLOCKNAME)总是返回标记为安全的模板数据。

具有自动转义功能的模板中的字符串文字被认为是不安全的,因为原生 Python 字符串不安全。


控制结构列表

控制结构是指控制程序流的所有事物 - 条件(即 if/elif/else)、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.items() %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

Python dict 可能与您想要的显示顺序不同。 如果顺序很重要,请使用 |dictsort 过滤器。

<dl>
{% for key, value in my_dict | dictsort %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

在 for 循环块内,您可以访问一些特殊变量:

多变的 描述
循环索引 循环的当前迭代。 (1 个索引)
循环索引0 循环的当前迭代。 (0 索引)
loop.revindex 从循环结束开始的迭代次数(1 个索引)
循环.revindex0 从循环结束开始的迭代次数(0 索引)
循环优先 如果第一次迭代为真。
循环.last 如果最后一次迭代,则为真。
循环长度 序列中的项目数。
循环 在序列列表之间循环的辅助函数。 请参阅下面的说明。
循环深度 指示当前渲染在递归循环中的深度。 从 1 级开始
循环深度0 指示当前渲染在递归循环中的深度。 从 0 级开始
loop.previtem 循环上一次迭代中的项目。 在第一次迭代期间未定义。
循环.nextitem 循环的以下迭代中的项目。 在上次迭代期间未定义。
loop.changed(*val) 如果之前使用不同的值调用(或根本不调用),则为真。

在 for 循环中,可以使用特殊的 loop.cycle 帮助器在每次循环中在字符串/变量列表之间循环:

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

从 Jinja 2.1 开始,存在一个额外的 cycle 帮助程序,它允许循环非绑定循环。 有关更多信息,请查看 全局函数列表

与 Python 不同,不可能在循环中 break 或 continue。 但是,您可以在迭代期间过滤序列,从而可以跳过项目。 以下示例跳过所有隐藏的用户:

{% 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 中,只要相应的循环 没有 break,就会执行 else 块。 由于 Jinja 循环无论如何都不能 break,因此选择了 else 关键字的稍微不同的行为。

也可以递归地使用循环。 如果您正在处理诸如站点地图或 RDFa 之类的递归数据,这将非常有用。 要递归使用循环,您基本上必须将 recursive 修饰符添加到循环定义中,并在要递归的地方使用新的迭代调用 loop 变量。

以下示例使用递归循环实现站点地图:

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

loop 变量总是指最近(最里面)的循环。 如果我们有不止一层的循环,我们可以通过在我们想要递归使用的循环之后编写 {% set outer_loop = loop %} 来重新绑定变量 loop。 然后,我们可以使用 模板:Outer loop(…) 调用它

请注意,循环中的赋值将在迭代结束时清除,并且不能超过循环范围。 旧版本的 Jinja 有一个错误,在某些情况下,赋值似乎可以工作。 这不受支持。 有关如何处理此问题的更多信息,请参阅 分配

如果您只想检查自上次迭代以来某个值是否已更改或将在下一次迭代中更改,则可以使用 previtem 和 nextitem:

{% for value in values %}
    {% if loop.previtem is defined and value > loop.previtem %}
        The value just increased!
    {% endif %}
    {{ value }}
    {% if loop.nextitem is defined and loop.nextitem > value %}
        The value will increase even more!
    {% endif %}
{% endfor %}

如果你只关心值是否改变了,使用 changed 更容易:

{% for entry in entries %}
    {% if loop.changed(entry.category) %}
        <h2>{{ entry.category }}</h2>
    {% endif %}
    <p>{{ entry.message }}</p>
{% endfor %}

如果

Jinja 中的 if 语句与 Python 的 if 语句相当。 以最简单的形式,您可以使用它来测试变量是否已定义,不为空且不为假:

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

对于多个分支,可以像在 Python 中一样使用 elif 和 else。 您也可以在那里使用更复杂的 Expressions

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

If 也可以用作 内联表达式 和用于 循环过滤


宏可与常规编程语言中的函数相媲美。 它们有助于将常用的习语放入可重用的函数中,以免重复自己(“DRY”)。

这是一个呈现表单元素的宏的小示例:

{% 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 但用于关键字参数。 所有未使用的关键字参数都存储在这个特殊变量中。
呼叫者
如果宏是从 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>
        <dt>Realname</dt>
        <dd>{{ user.realname|e }}</dd>
        <dt>Description</dt>
        <dd>{{ user.description }}</dd>
    </dl>
{% endcall %}

过滤器

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

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

作业

在代码块中,您还可以为变量赋值。 顶层(块、宏或循环之外)的分配与顶层宏一样从模板导出,并且可以由其他模板导入。

作业使用 set 标签并且可以有多个目标:

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

范围界定行为

请记住,不可能在块内设置变量并让它们显示在块外。 这也适用于循环。 该规则的唯一例外是不引入作用域的 if 语句。 因此,以下模板不会按照您的预期执行:

{% set iterated = false %}
{% for item in seq %}
    {{ item }}
    {% set iterated = true %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %}

Jinja 语法不可能做到这一点。 而是使用替代结构,如循环 else 块或特殊的 loop 变量:

{% for item in seq %}
    {{ item }}
{% else %}
    did not iterate
{% endfor %}

从 2.10 版开始,可以使用名称空间对象处理更复杂的用例,这些对象允许跨范围传播更改:

{% set ns = namespace(found=false) %}
{% for item in items %}
    {% if item.check_something() %}
        {% set ns.found = true %}
    {% endif %}
    * {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}

请注意,set 标签中的 obj.attr 符号只允许用于命名空间对象; 尝试在任何其他对象上分配属性将引发异常。

2.10 新功能: 增加了对命名空间对象的支持


块分配

2.8 版中的新功能。


从 Jinja 2.8 开始,还可以使用块赋值来将块的内容捕获到变量名中。 这在某些情况下作为宏的替代方案很有用。 在这种情况下,您无需使用等号和值,而只需写入变量名称,然后写入所有内容,直到捕获到 {% endset %}

例子:

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

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

在 2.10 版中更改。


从 Jinja 2.10 开始,块分配支持过滤器。

例子:

{% set reply | wordwrap %}
    You wrote:
    {{ message }}
{% endset %}

扩展

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

请参阅上面有关 模板继承 的部分。


块用于继承并同时充当占位符和替换。 它们在 模板继承 部分中有详细记录。


包括

include 标签可用于包含模板并将该文件的渲染内容返回到当前命名空间:

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

默认情况下,包含的模板可以访问活动上下文的变量。 有关导入和包含的上下文行为的更多详细信息,请参阅 导入上下文行为

从 Jinja 2.2 开始,你可以用 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 %}

2.4 版更改: 如果模板对象被传递到模板上下文,您可以使用 include 包含该对象。


进口

Jinja 支持将常用代码放入宏中。 这些宏可以进入不同的模板并从那里导入。 这与 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>

以一个或多个下划线开头的宏和变量是私有的,不能导入。

2.4 版更改: 如果模板对象被传递到模板上下文,您可以从该对象导入。


导入上下文行为

默认情况下,包含的模板会通过当前上下文,而导入的模板不会。 这样做的原因是导入与包含不同,是缓存的; 因为导入通常仅用作包含宏的模块。

可以显式更改此行为:通过将 with context 或 without context 添加到 import/include 指令,可以将当前上下文传递给模板并自动禁用缓存。

这里有两个例子:

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

笔记

在 Jinja 2.0 中,传递给包含模板的上下文不包含模板中定义的变量。 事实上,这不起作用:

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

包含的模板 render_box.html而不是 能够访问 Jinja 2.0 中的 box。 从 Jinja 2.1 开始,render_box.html ' 能够这样做。


表达式

Jinja 允许在任何地方使用基本表达式。 它们的工作方式与常规 Python 非常相似; 即使您不使用 Python,您也应该对它感到满意。

文字

最简单的表达式形式是文字。 文字是 Python 对象的表示,例如字符串和数字。 存在以下文字:

"Hello World"

两个双引号或单引号之间的所有内容都是字符串。 当您需要模板中的字符串时,它们很有用(例如 作为函数调用和过滤器的参数,或者只是扩展或包含模板)。

42 / 123_456

整数是没有小数部分的整数。 '_' 字符可用于分隔组以提高可读性。

42.23 / 42.1e2 / 123_456.789

浮点数可以使用“.”书写。 作为小数点。 它们也可以用科学记数法用大写或小写“e”来表示指数部分。 '_' 字符可用于分隔组以提高可读性,但不能用于指数部分。

['list', 'of', 'objects']

两个括号之间的所有内容都是一个列表。 列表对于存储要迭代的顺序数据很有用。 例如,您可以使用 for(和)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': 'and', 'value': 'pairs'}

Python 中的 dict 是组合键和值的结构。 键必须是唯一的,并且始终只有一个值。 字典在模板中很少使用; 它们在一些罕见的情况下很有用,例如 xmlattr() 过滤器。

true / false

true 始终为真,false 始终为假。

笔记

特殊常量 true、false 和 none 确实是小写的。 因为这在过去引起了混乱,(True 过去常常扩展到一个被认为是假的未定义变量),现在这三个都可以写在标题中(True, ]假和无)。 然而,为了一致性,(所有 Jinja 标识符都是小写)你应该使用小写版本。


数学

Jinja 允许您使用值进行计算。 这在模板中很少有用,但为了完整性而存在。 支持以下运算符:

+

将两个对象相加。 通常对象是数字,但如果两者都是字符串或列表,您可以通过这种方式连接它们。 然而,这不是连接字符串的首选方式! 对于字符串连接,请查看 ~ 运算符。 模板:1 + 12

-

从第一个数字中减去第二个数字。 模板:3 - 21

/

将两个数字相除。 返回值将是一个浮点数。 模板:1 / 2模板:0.5

//

将两个数字相除并返回截断后的整数结果。 模板:20 // 72

%

计算整数除法的余数。 模板:11 % 74

*

将左操作数与右操作数相乘。 模板:2 * 2 将返回 4。 这也可用于多次重复字符串。 模板:'=' * 80 将打印一个由 80 个等号组成的条。

**

将左操作数提高到右操作数的幂。 模板:2**3 将返回 8

与 Python 不同,链式 pow 是从左到右计算的。 模板:3**3**3 在 Jinja 中被评估为 (3**3)**3,但在 Python 中将被评估为 3**(3**3)。 在 Jinja 中使用括号来明确您想要的顺序。 通常最好在 Python 中进行扩展数学并将结果传递给 render,而不是在模板中进行。

如果可以引入升级路径,此行为将来可能会更改以匹配 Python。


比较

==
比较两个对象是否相等。
!=
比较两个对象的不等式。
>
true 如果左侧大于右侧。
>=
true 如果左侧大于或等于右侧。
<
true 如果左侧低于右侧。
<=
true 如果左侧低于或等于右侧。


逻辑

对于 if 语句、for 过滤和 if 表达式,组合多个表达式会很有用:

and
如果左右操作数为真,则返回真。
or
如果左操作数或右操作数为真,则返回真。
not
否定一个陈述(见下文)。
(expr)
括号对表达式进行分组。

笔记

isin 运算符也支持使用中缀表示法的否定:foo is not barfoo not in bar 而不是 not foo is bar 和 [ X138X]。 所有其他表达式都需要前缀符号:not (foo and bar).


其他运营商

以下运算符非常有用,但不属于其他两类:

in

执行序列/映射遏制测试。 如果左侧操作数包含在右侧,则返回 true。 例如,{{ 1 in [1, 2, 3] }} 将返回 true。

is

执行 测试

|(管道,竖条)

应用 过滤器

~(波浪号)

将所有操作数转换为字符串并连接它们。

模板:"Hello " ~ name ~ "!" 将返回(假设 name 设置为 'John'Hello John!

()

调用可调用对象:模板:Post.render()。 在括号内,您可以像在 Python 中一样使用位置参数和关键字参数:

模板:Post.render(user, full=true)

. / []

获取对象的属性。 (参见 变量


如果表达式

也可以使用内联 if 表达式。 这些在某些情况下很有用。 例如,如果定义了变量,您可以使用它从一个模板扩展,否则从默认布局模板扩展:

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

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

else 部分是可选的。 如果未提供,则 else 块会隐式计算为 Undefined 对象(无论环境中的 undefined 设置为什么):

{{ "[{}]".format(page.title) if page.title }}

Python方法

您还可以使用在变量类型上定义的任何方法。 从方法调用返回的值用作表达式的值。 这是一个使用在字符串上定义的方法的示例(其中 page.title 是一个字符串):

{{ page.title.capitalize() }}

这适用于用户定义类型的方法。 例如,如果 Foo 类型的变量 f 上定义了一个方法 bar,您可以执行以下操作:

{{ f.bar(value) }}

运算符方法也按预期工作。 例如, % 为字符串实现 printf 样式:

{{ "Hello, %s!" % name }}

尽管在这种情况下您应该更喜欢 .format 方法(这在渲染模板的上下文中有点做作):

{{ "Hello, {}!".format(name) }}

内置过滤器列表

内置测试列表

全局函数列表

默认情况下,以下函数在全局范围内可用:

jinja-globals.range([start, ]stop[, step])

返回一个包含整数等差数列的列表。 range(i, j) 返回 [i, i+1, i+2, ..., j-1]; 开始 (!) 默认为 0。 当给出 step 时,它指定增量(或减量)。 例如,range(4)range(0, 4, 1) 返回 [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>
jinja-globals.lipsum(n=5, html=True, min=20, max=100)
为模板生成一些lorem ipsum。 默认情况下,会生成 5 个 HTML 段落,每个段落的长度在 20 到 100 个单词之间。 如果 html 为 False,则返回常规文本。 这对于生成用于布局测试的简单内容很有用。
jinja-globals.dict(\**items)
字典文字的方便替代品。 {'foo': 'bar'}dict(foo='bar') 相同。
class jinja-globals.cycler(\*items)

通过一次产生一个值来循环遍历值,然后在到达结束时重新开始。

类似于 loop.cycle,但可以在循环外或跨多个循环使用。 例如,在列表中呈现文件夹和文件列表,交替为它们提供“奇数”和“偶数”类。

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

items – 每个位置参数将按照每个周期给定的顺序产生。

2.1 版中的新功能。

property current

返回当前项目。 相当于下次调用 next() 时会返回的项。

next()

返回当前项目,然后前进 current 到下一个项目。

reset()

将当前项目重置为第一个项目。

class jinja-globals.joiner(sep=', ')

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

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

2.1 版中的新功能。

class jinja-globals.namespace(...)

创建一个允许使用 {% set %} 标签进行属性分配的新容器:

{% set ns = namespace() %}
{% set ns.foo = 'bar' %}

这样做的主要目的是允许将值从循环体内部传送到外部作用域。 初始值可以提供为 dict、关键字参数或两者(与 Python 的 dict 构造函数相同的行为):

{% set ns = namespace(found=false) %}
{% for item in items %}
    {% if item.check_something() %}
        {% set ns.found = true %}
    {% endif %}
    * {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}

2.10 版中的新功能。


扩展

以下部分介绍了可由应用程序启用的内置 Jinja 扩展。 应用程序还可以提供本文档未涵盖的进一步扩展; 在这种情况下,应该有一个单独的文档来解释所说的 扩展

国际化

如果启用了 i18n Extension,则可以将模板中的文本标记为可翻译。 要将部分标记为可翻译,请使用 trans 块:

{% trans %}Hello, {{ user }}!{% endtrans %}

在块内部,不允许使用任何语句,只允许使用文本和简单的变量标签。

变量标签只能是名称,不能是属性访问、过滤器或其他表达式。 要使用表达式,请将其绑定到 trans 标记中的名称以在块中使用。

{% trans user=user.username %}Hello, {{ user }}!{% endtrans %}

要绑定多个表达式,请用逗号分隔每个表达式 (,)。

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

要复数,请指定由 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 %}

在翻译文本块时,空格和换行符会导致难以阅读且容易出错的翻译字符串。 为避免这种情况,可以将 trans 块标记为修剪,这将用单个空格替换所有换行符和它们周围的空格,并删除前导和尾随空格。

{% trans trimmed book_title=book.title %}
    This is {{ book_title }}.
    You should read it!
{% endtrans %}

这会导致翻译文件中出现 This is %(book_title)s. You should read it!

如果全局启用修剪,则可以使用 notrimmed 修饰符为块禁用它。

2.10 版新功能: 添加了 trimmednotrimmed 修饰符。


可以使用以下函数翻译表达式中的字符串:

  • gettext:翻译单个字符串
  • ngettext:翻译一个可复数的字符串
  • _gettext 的别名

您可以像这样打印翻译后的字符串:

{{ _("Hello, World!") }}

要使用占位符,请使用 format 过滤器。

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

始终对 format 使用关键字参数,因为其他语言可能不会以相同的顺序使用这些单词。

如果激活了 New Style Gettext 调用,则使用占位符会更容易。 格式化是 gettext 调用的一部分,而不是使用 format 过滤器。

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

除了给定的参数之外,ngettext 函数的格式字符串自动接收计数作为 num 参数。


表达式语句

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

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

循环控制

如果应用程序启用了 Loop Controls,则可以在循环中使用 break 和 continue。 当到达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)。


调试语句

如果启用了 Debug Extension,一个 {% debug %} 标签将可用于转储当前上下文以及可用的过滤器和测试。 这对于在不设置调试器的情况下查看模板中可用的内容很有用。

<pre>{% debug %}</pre>
{'context': {'cycler': <class 'jinja2.utils.Cycler'>,
             ...,
             'namespace': <class 'jinja2.utils.Namespace'>},
 'filters': ['abs', 'attr', 'batch', 'capitalize', 'center', 'count', 'd',
             ..., 'urlencode', 'urlize', 'wordcount', 'wordwrap', 'xmlattr'],
 'tests': ['!=', '<', '<=', '==', '>', '>=', 'callable', 'defined',
           ..., 'odd', 'sameas', 'sequence', 'string', 'undefined', 'upper']}

有声明

2.3 版中的新功能。


with 语句可以创建一个新的内部作用域。 在此范围内设置的变量在范围外不可见。

简而言之:

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

因为在作用域的开头设置变量是很常见的,所以您可以在 with 语句中执行此操作。 下面两个例子是等价的:

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

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

关于范围界定的重要说明。 在 2.9 之前的 Jinja 版本中,将一个变量引用到另一个变量的行为会产生一些意想不到的后果。 特别是一个变量可以引用与块的开头语句相同的另一个变量。 这导致了清理范围行为的问题,此后已得到改进。 特别是在较新的 Jinja 版本中,以下代码总是从 with 块外部引用变量 a:

{% with a={}, b=a.attribute %}...{% endwith %}

在早期的 Jinja 版本中, b 属性将引用第一个属性的结果。 如果您依赖此行为,您可以重写它以使用 set 标签:

{% with a={} %}
    {% set b = a.attribute %}
{% endwith %}

延期

在旧版本的 Jinja(2.9 之前)中,需要通过扩展启用此功能。 它现在默认启用。


自动转义覆盖

2.4 版中的新功能。


如果需要,您可以从模板中激活和停用自动转义。

例子:

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

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

在 endautoescape 之后,行为恢复到之前的状态。

延期

在旧版本的 Jinja(2.9 之前)中,需要通过扩展启用此功能。 它现在默认启用。