模板设计器文档¶
本文档描述了模板引擎的语法和语义,对于创建 Jinja 模板的人来说,它将是最有用的参考。由于模板引擎非常灵活,应用程序的配置可能与此处展示的代码在定界符和未定义值的处理方面略有不同。
概要¶
Jinja 模板只是一个文本文件。Jinja 可以生成任何基于文本的格式(HTML、XML、CSV、LaTeX 等)。Jinja 模板不需要有特定的扩展名:.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 }}
{# a comment #}
</body>
</html>
以下示例显示了默认配置设置。应用程序开发人员可以将语法配置从 {% foo %}
更改为 <% foo %>
或类似内容。
有几种定界符。默认的 Jinja 定界符配置如下
行语句和注释 也是可能的,尽管它们没有默认的前缀字符。要使用它们,请在创建 Environment
时设置 line_statement_prefix
和 line_comment_prefix
。
模板文件扩展名¶
如上所述,任何文件都可以加载为模板,无论文件扩展名是什么。添加一个 .jinja
扩展名,如 user.html.jinja
可能使某些 IDE 或编辑器插件更容易使用,但不是必需的。稍后介绍的自动转义可以基于文件扩展名应用,因此在这种情况下,您需要考虑额外的后缀。
识别模板的另一个好启发式方法是,它们位于 templates
文件夹中,无论扩展名是什么。这是项目的常见布局。
变量¶
模板变量由传递给模板的上下文词典定义。
只要应用程序传递了变量,您就可以在模板中修改变量。变量可能具有您可以访问的属性或元素。变量具有的属性在很大程度上取决于提供该变量的应用程序。
除了标准 Python __getitem__
“下标”语法 ([]
) 之外,您还可以使用点 (.
) 来访问变量的属性。
以下行执行相同操作
{{ foo.bar }}
{{ foo['bar'] }}
重要的是要知道,外部双大括号不是变量的一部分,而是打印语句的一部分。如果您在标签内访问变量,请不要在它们周围加上大括号。
如果变量或属性不存在,您将获得一个未定义的值。您可以对这种值执行的操作取决于应用程序配置:如果打印或迭代,则默认行为是求值为一个空字符串,并且对于任何其他操作都将失败。
实现
为了方便起见,Jinja 中的 foo.bar
在 Python 层上执行以下操作
检查
foo
上是否有名为bar
的属性 (getattr(foo, 'bar')
)如果没有,请检查
foo
中是否有项目'bar'
(foo.__getitem__('bar')
)如果没有,则返回一个未定义的对象。
foo['bar']
的工作方式大致相同,但顺序略有不同
检查
'bar'
项在foo
中。(foo.__getitem__('bar')
)如果没有,检查
foo
上名为bar
的属性。(getattr(foo, 'bar')
)如果没有,则返回一个未定义的对象。
如果对象具有同名项和属性,这很重要。此外,attr()
过滤器仅查找属性。
过滤器¶
变量可以通过过滤器修改。过滤器通过管道符号 (|
) 与变量分隔,并且可以在括号中具有可选参数。可以链接多个过滤器。一个过滤器的输出应用于下一个过滤器。
例如,{{ name|striptags|title }}
将从变量 name
中删除所有 HTML 标记,并将输出标题化 (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) %}
下面的内置测试列表描述了所有内置测试。
空格控制¶
在默认配置中
如果存在,则删除一个尾随换行符
其他空格(空格、制表符、换行符等)保持不变
如果应用程序将 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
是从 1
到 9
的数字列表,输出将为 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 -%}
标签末尾的减号会清除所有空格和换行符,这些空格和换行符位于原始数据第一个字符之前。
行语句¶
如果应用程序启用了行语句,则可以将一行标记为语句。例如,如果将行语句前缀配置为 #
,则以下两个示例是等效的
<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 %}
© 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¶
在有多个 {% 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.
块嵌套和范围¶
可以嵌套块以实现更复杂的布局。但是,默认情况下,块可能无法访问外部范围的变量
{% for item in seq %}
<li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}
此示例将输出空的 <li>
项,因为 item
在块内不可用。原因是如果块被子模板替换,则会出现一个未在块中定义或传递给上下文的变量。
从 Jinja 2.2 开始,您可以通过将 scoped
修饰符添加到块声明中,将块设置为“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.txt
或 issue.txt
会引发 TemplateRuntimeError
,因为它们未覆盖 body
块。渲染 bug_report.txt
会成功,因为它确实覆盖了该块。
与 scoped
结合使用时,required
修饰符必须放在范围修饰符之后。以下是一些有效的示例
{% block body scoped %}{% endblock %}
{% block body required %}{% endblock %}
{% block body scoped required %}{% endblock %}
模板对象¶
extends
、include
和 import
可以采用模板对象,而不是要加载的模板的名称。这在某些高级情况下可能很有用,因为你可以使用 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")
return user_detail.render(layout=layout)
{% extends layout %}
请注意,extends
传递的是包含传递给 render
的模板对象的变量,而不是字符串。
HTML 转义¶
从模板生成 HTML 时,总是存在变量会包含影响结果 HTML 的字符的风险。有两种方法
手动转义每个变量;或
默认情况下自动转义所有内容。
Jinja 支持这两种方法。使用哪种方法取决于应用程序配置。默认配置是不进行自动转义;出于各种原因
转义除安全值之外的所有内容还意味着 Jinja 会转义已知不包含 HTML 的变量(例如数字、布尔值),这可能会极大地影响性能。
有关变量安全性的信息非常脆弱。可能会发生这种情况:通过强制转换安全值和不安全值,返回值是双重转义的 HTML。
使用手动转义¶
如果启用了手动转义,则您有责任在需要时转义变量。转义什么?如果您有一个变量可能包含以下任何字符(>
、<
、&
或"
),则应转义它,除非该变量包含格式良好的受信任 HTML。转义通过将变量通过|e
过滤器来实现
{{ user.username|e }}
使用自动转义¶
启用自动转义后,默认情况下所有内容都将被转义,但明确标记为安全的除外。变量和表达式可以在以下位置标记为安全
应用程序使用
markupsafe.Markup
在上下文词典中模板,使用
|safe
过滤器。
如果您标记为安全的字符串通过不理解该标记的其他 Python 代码传递,则该标记可能会丢失。请注意您的数据何时标记为安全以及在到达模板之前如何处理它。
如果一个值已被转义但未标记为安全,则仍会进行自动转义,并导致字符双重转义。如果您知道您有已经安全但未标记的数据,请务必将其包装在Markup
中或使用|safe
过滤器。
Jinja 函数(宏、super
、self.BLOCKNAME
)始终返回标记为安全的模板数据。
具有自动转义的模板中的字符串文字被认为不安全,因为本机 Python 字符串不安全。
控制结构列表¶
控制结构是指控制程序流的所有内容 - 条件(即 if/elif/else)、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.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>
Python 字典可能不会按您希望显示的顺序排列。如果顺序很重要,请使用 |dictsort
过滤器。
<dl>
{% for key, value in my_dict | dictsort %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>
在 for 循环块内,您可以访问一些特殊变量
变量 |
说明 |
---|---|
|
循环的当前迭代。(从 1 开始编号) |
|
循环的当前迭代。(从 0 开始编号) |
|
循环末尾的迭代次数(从 1 开始编号) |
|
循环末尾的迭代次数(从 0 开始编号) |
|
如果是第一次迭代,则为 True。 |
|
如果是最后一次迭代,则为 True。 |
|
序列中的项目数。 |
|
一个在序列列表之间循环的辅助函数。请参见下面的说明。 |
|
指示渲染当前在递归循环中的深度。从第 1 级开始 |
|
指示渲染当前在递归循环中的深度。从第 0 级开始 |
|
循环前一次迭代中的项目。在第一次迭代期间未定义。 |
|
循环下一次迭代中的项目。在最后一次迭代期间未定义。 |
|
如果先前使用不同的值调用(或根本未调用),则为 True。 |
在 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 有一个错误,在某些情况下,它似乎可以进行赋值。这是不受支持的。有关如何处理此问题,请参阅 Assignments 以获取更多信息。
如果你只想检查某个值自上次迭代以来是否已更改或将在下次迭代中更改,你可以使用 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 %}
对于多个分支,elif
和 else
可以像在 Python 中一样使用。你也可以在那里使用更复杂的 Expressions
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
If 还可以用作 inline expression 和 loop filtering。
宏¶
宏可与常规编程语言中的函数相媲美。它们可用于将经常使用的惯用语放入可重用函数中,以避免重复自己(“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
变量中。kwargs
类似于
varargs
,但用于关键字参数。所有未使用的关键字参数都存储在这个特殊变量中。caller
如果宏是从 call 标记调用的,则调用者将作为可调用宏存储在这个变量中。
宏还会公开一些其内部详细信息。宏对象上提供以下属性
name
宏的名称。
{{ input.name }}
将打印input
。arguments
宏接受的参数名称的元组。
catch_kwargs
如果宏接受额外的关键字参数(即:访问特殊
kwargs
变量),则此值为true
。catch_varargs
如果宏接受额外的定位参数(即:访问特殊
varargs
变量),则此值为true
。caller
如果宏访问特殊
caller
变量,并且可以从 call 标记调用,则此值为true
。
如果宏名称以下划线开头,则不会导出该宏,并且无法导入该宏。
由于 Jinja 中作用域的工作方式,子模板中的宏不会覆盖父模板中的宏。以下内容将输出“LAYOUT”,而不是“CHILD”。
layout.txt
¶{% macro foo() %}LAYOUT{% endmacro %}
{% block body %}{% endblock %}
child.txt
¶{% extends 'layout.txt' %}
{% macro foo() %}CHILD{% endmacro %}
{% block body %}{{ foo() }}{% endblock %}
调用¶
在某些情况下,将宏传递给另一个宏可能很有用。为此,可以使用特殊的 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 %}
接受参数的过滤器可以这样调用
{% filter center(100) %}Center this{% 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 }}
请注意,obj.attr
在 set
标记中的符号仅允许用于命名空间对象;尝试为任何其他对象分配属性会引发异常。
变更日志
在 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 goes here.
{% include 'footer.html' %}
默认情况下,包含的模板可以访问当前模板的上下文。使用 without context
来使用单独的上下文。 with context
也有效,但这是默认行为。请参阅 导入上下文行为。
包含的模板可以 extend
另一个模板并覆盖该模板中的块。但是,当前模板无法覆盖包含的模板输出的任何块。
如果模板不存在,请使用 ignore missing
忽略该语句。它必须放在上下文可见性语句之前。
{% include "sidebar.html" without context %}
{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
如果给出了模板列表,则会按顺序尝试每个模板,直到一个不缺失。这可以与 ignore missing
一起使用,以忽略如果所有模板都不存在的情况。
{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
一个变量(带有模板名称或模板对象)也可以传递给该语句。
导入¶
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 循环(和 with)
<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
、False
和 None
)。但是,为了保持一致性(所有 Jinja 标识符都为小写),您应该使用小写版本。
数学¶
Jinja 允许您使用值进行计算。这在模板中很少有用,但为了完整性而存在。支持以下运算符
+
将两个对象相加。通常对象是数字,但如果两者都是字符串或列表,则可以这样连接它们。但是,这不是连接字符串的首选方式!对于字符串连接,请查看
~
运算符。{{ 1 + 1 }}
为2
。-
从第一个数字中减去第二个数字。
{{ 3 - 2 }}
为1
。/
将两个数字相除。返回值将是浮点数。
{{ 1 / 2 }}
为{{ 0.5 }}
。//
将两个数字相除并返回截断的整数结果。
{{ 20 // 7 }}
为2
。%
计算整数除法的余数。
{{ 11 % 7 }}
为4
。*
将左操作数与右操作数相乘。
{{ 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
如果左右操作数都为 true,则返回 true。
or
如果左右操作数之一为 true,则返回 true。
not
否定一个语句(见下文)。
(expr)
括号对一个表达式进行分组。
注意
运算符 is
和 in
也支持使用中缀表示法进行否定:foo is not bar
和 foo not in bar
,而不是 not foo is bar
和 not foo in bar
。所有其他表达式需要使用前缀表示法: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-filters.abs(x, /)¶
返回参数的绝对值。
- jinja-filters.attr(obj: Any, name: str) jinja2.runtime.Undefined | Any ¶
获取对象的属性。
foo|attr("bar")
的工作原理类似于foo.bar
,只是始终返回一个属性,并且不查找项目。有关更多详细信息,请参阅 关于订阅的注释。
- jinja-filters.batch(value: 't.Iterable[V]', linecount: int, fill_with: 't.Optional[V]' = None) 't.Iterator[t.List[V]]' ¶
批量处理项目的过滤器。它的工作方式与
slice
非常相似,只是相反。它返回一个列表,其中包含给定数量的项目。如果你提供第二个参数,则将其用于填充缺失的项目。请参阅此示例<table> {%- for row in items|batch(3, ' ') %} <tr> {%- for column in row %} <td>{{ column }}</td> {%- endfor %} </tr> {%- endfor %} </table>
- jinja-filters.default(value: V, default_value: V = '', boolean: bool = False) V ¶
如果值未定义,它将返回传递的默认值,否则返回变量的值
{{ my_variable|default('my_variable is not defined') }}
如果变量已定义,这将输出
my_variable
的值,否则输出'my_variable is not defined'
。如果您想对求值为 false 的变量使用 default,则必须将第二个参数设置为true
{{ ''|default('the string was empty', true) }}
变更日志
在 2.11 版本中更改:现在可以通过
Environment
使用ChainableUndefined
进行配置,以使default
过滤器在链中可能包含未定义值的嵌套元素和属性上工作,而不会出现UndefinedError
。- 别名:
d
- jinja-filters.dictsort(value: Mapping[K, V], case_sensitive: bool = False, by: 'te.Literal["key", "value"]' = 'key', reverse: bool = False) List[Tuple[K, V]] ¶
对字典进行排序并生成 (键、值) 对。Python 字典可能不是您希望在其中显示它们的顺序,因此请先对它们进行排序。
{% for key, value in mydict|dictsort %} sort the dict by key, case insensitive {% for key, value in mydict|dictsort(reverse=true) %} sort the dict by key, case insensitive, reverse order {% for key, value in mydict|dictsort(true) %} sort the dict by key, case sensitive {% for key, value in mydict|dictsort(false, 'value') %} sort the dict by value, case insensitive
- jinja-filters.escape(value)¶
用 HTML 安全序列替换字符串中的字符
&
、<
、>
、'
和"
。如果您需要在 HTML 中显示可能包含此类字符的文本,请使用此方法。如果对象具有
__html__
方法,则会调用该方法,并且假定返回值已经对 HTML 安全。- 参数:
s – 要转换为字符串并转义的对象。
- 返回:
带有转义文本的
Markup
字符串。- 别名:
e
- jinja-filters.filesizeformat(value: str | float | int, binary: bool = False) str ¶
将值格式化为“人类可读”的文件大小(即 13 kB、4.1 MB、102 字节等)。默认情况下使用十进制前缀(兆、吉等),如果第二个参数设置为
True
,则使用二进制前缀(兆字节、吉字节)。
- jinja-filters.first(seq: 't.Iterable[V]') 't.Union[V, Undefined]' ¶
返回序列的第一项。
- jinja-filters.float(value: Any, default: float = 0.0) float ¶
将值转换为浮点数。如果转换失败,它将返回
0.0
。你可以使用第一个参数覆盖此默认值。
- jinja-filters.forceescape(value: 't.Union[str, HasHTML]') markupsafe.Markup ¶
强制 HTML 转义。这可能会对变量进行双重转义。
- jinja-filters.format(value: str, *args: Any, **kwargs: Any) str ¶
将给定值应用于 printf 样式 格式字符串,如
string % values
。{{ "%s, %s!"|format(greeting, name) }} Hello, World!
在大多数情况下,使用
%
运算符或str.format()
会更方便、更高效。{{ "%s, %s!" % (greeting, name) }} {{ "{}, {}!".format(greeting, name) }}
- jinja-filters.groupby(value: 't.Iterable[V]', attribute: str | int, default: Any | None = None, case_sensitive: bool = False) 't.List[_GroupTuple]' ¶
使用 Python 的
itertools.groupby()
按属性对对象序列进行分组。该属性可以使用点表示法进行嵌套访问,例如"address.city"
。与 Python 的groupby
不同,值会先进行排序,因此每个唯一值只返回一个组。例如,带有
city
属性的User
对象列表可以按组呈现。在此示例中,grouper
指的是该组的city
值。<ul>{% for city, items in users|groupby("city") %} <li>{{ city }} <ul>{% for user in items %} <li>{{ user.name }} {% endfor %}</ul> </li> {% endfor %}</ul>
groupby
产生(grouper, list)
的 namedtuple,可用于代替上述元组拆包。grouper
是属性的值,list
是具有该值的对象。<ul>{% for group in users|groupby("city") %} <li>{{ group.grouper }}: {{ group.list|join(", ") }} {% endfor %}</ul>
如果列表中的对象没有给定的属性,你可以指定
default
值。<ul>{% for city, items in users|groupby("city", default="NY") %} <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li> {% endfor %}</ul>
与
sort()
过滤器类似,默认情况下,排序和分组不区分大小写。key
对于每个组,其大小写与该值组中第一个对象的大小写相同。例如,如果用户列表的城市为["CA", "NY", "ca"]
,则“CA”组将有两个值。可以通过传递case_sensitive=True
来禁用此功能。3.1 版中已更改:添加了
case_sensitive
参数。默认情况下,排序和分组不区分大小写,与执行比较的其他过滤器匹配。变更日志
3.0 版中已更改:添加了
default
参数。2.6 版中已更改:该属性支持点符号,用于嵌套访问。
- jinja-filters.indent(s: str, width: int | str = 4, first: bool = False, blank: bool = False) str ¶
返回一个字符串的副本,其中每一行都缩进 4 个空格。默认情况下,第一行和空行不会缩进。
- 参数:
width – 缩进的空格数或字符串。
first – 不要跳过缩进第一行。
blank – 不要跳过缩进空行。
变更日志
在版本 3.0 中更改:
width
可以是字符串。在版本 2.10 中更改: 默认情况下,空行不会缩进。
将
indentfirst
参数重命名为first
。
- jinja-filters.int(value: Any, default: int = 0, base: int = 10) int ¶
将值转换为整数。如果转换不起作用,它将返回
0
。您可以使用第一个参数覆盖此默认值。您还可以在第二个参数中覆盖默认基数 (10),该参数处理带有前缀的输入,例如 0b、0o 和 0x,分别表示基数 2、8 和 16。对于十进制数和非字符串值,将忽略基数。
- jinja-filters.items(value: Mapping[K, V] | jinja2.runtime.Undefined) Iterator[Tuple[K, V]] ¶
返回映射的
(key, value)
项的迭代器。x|items
与x.items()
相同,但如果x
未定义,则返回一个空迭代器。如果您希望使用另一种编程语言中的 Jinja 实现来渲染模板,而该语言的映射类型没有
.items()
方法,则此过滤器很有用。<dl> {% for key, value in my_dict|items %} <dt>{{ key }} <dd>{{ value }} {% endfor %} </dl>
在版本 3.1 中添加。
- jinja-filters.join(value: Iterable[Any], d: str = '', attribute: str | int | NoneType = None) str ¶
返回一个字符串,它是序列中字符串的连接。元素之间的分隔符默认情况下是一个空字符串,你可以使用可选参数定义它
{{ [1, 2, 3]|join('|') }} -> 1|2|3 {{ [1, 2, 3]|join }} -> 123
还可以连接对象的某些属性
{{ users|join(', ', attribute='username') }}
变更日志
在版本 2.6 中添加: 添加了
attribute
参数。
- jinja-filters.last(seq: 't.Reversible[V]') 't.Union[V, Undefined]' ¶
返回序列的最后一个项目。
注意:不适用于生成器。你可能希望明确地将其转换为列表
{{ data | selectattr('name', '==', 'Jinja') | list | last }}
- jinja-filters.length(obj, /)¶
返回容器中的项目数。
- 别名:
计数
- jinja-filters.list(value: 't.Iterable[V]') 't.List[V]' ¶
将值转换为列表。如果它是字符串,则返回的列表将是字符列表。
- jinja-filters.map(value: Iterable[Any], *args: Any, **kwargs: Any) Iterable[Any] ¶
对一系列对象应用过滤器或查找属性。在处理对象列表但你只对其中的某个值感兴趣时,这很有用。
基本用法是映射到属性。假设你有一份用户列表,但你只对用户名列表感兴趣
Users on this page: {{ users|map(attribute='username')|join(', ') }}
如果列表中的对象没有给定的属性,你可以指定
default
值。{{ users|map(attribute="username", default="Anonymous")|join(", ") }}
或者,你可以通过传递过滤器的名称和后面的参数来让它调用过滤器。一个很好的例子是在序列上应用文本转换过滤器
Users on this page: {{ titles|map('lower')|join(', ') }}
类似于生成器解析,例如
(u.username for u in users) (getattr(u, "username", "Anonymous") for u in users) (do_lower(x) for x in titles)
变更日志
2.11.0 版本中已更改: 添加了
default
参数。2.7 版本中已添加。
- jinja-filters.max(value: 't.Iterable[V]', case_sensitive: bool = False, attribute: str | int | NoneType = None) 't.Union[V, Undefined]' ¶
从序列中返回最大的项目。
{{ [1, 2, 3]|max }} -> 3
- 参数:
case_sensitive – 将大小写字符串视为不同的字符串。
attribute – 获取具有此属性的最大值的项目。
- jinja-filters.min(value: 't.Iterable[V]', case_sensitive: bool = False, attribute: str | int | NoneType = None) 't.Union[V, Undefined]' ¶
返回序列中最小的项。
{{ [1, 2, 3]|min }} -> 1
- 参数:
case_sensitive – 将大小写字符串视为不同的字符串。
属性 – 获取具有此属性最小值的项。
- jinja-filters.random(seq: 't.Sequence[V]') 't.Union[V, Undefined]' ¶
从序列中返回一个随机项。
- jinja-filters.reject(value: 't.Iterable[V]', *args: Any, **kwargs: Any) 't.Iterator[V]' ¶
通过对每个对象应用测试来筛选对象序列,并拒绝测试成功的对象。
如果未指定测试,则每个对象将被评估为布尔值。
示例用法
{{ numbers|reject("odd") }}
类似于生成器解析,例如
(n for n in numbers if not test_odd(n))
变更日志
2.7 版本中已添加。
- jinja-filters.rejectattr(value: 't.Iterable[V]', *args: Any, **kwargs: Any) 't.Iterator[V]' ¶
通过将测试应用到每个对象的指定属性并拒绝测试成功的对象,筛选一系列对象。
如果没有指定测试,则属性的值将被评估为布尔值。
{{ users|rejectattr("is_active") }} {{ users|rejectattr("email", "none") }}
类似于生成器解析,例如
(u for user in users if not user.is_active) (u for user in users if not test_none(user.email))
变更日志
2.7 版本中已添加。
- jinja-filters.replace(s: str, old: str, new: str, count: int | None = None) str ¶
返回一个值副本,其中所有子字符串都已替换为新的子字符串。第一个参数是要替换的子字符串,第二个是替换字符串。如果给出了可选的第三个参数
count
,则只替换前count
个出现。{{ "Hello World"|replace("Hello", "Goodbye") }} -> Goodbye World {{ "aaaaargh"|replace("a", "d'oh, ", 2) }} -> d'oh, d'oh, aaargh
- jinja-filters.round(value: float, precision: int = 0, method: 'te.Literal["common", "ceil", "floor"]' = 'common') float ¶
将数字舍入到给定的精度。第一个参数指定精度(默认为
0
),第二个参数指定舍入方法'common'
向上或向下舍入'ceil'
始终向上舍入'floor'
始终向下舍入
如果您未指定方法,则使用
'common'
。{{ 42.55|round }} -> 43.0 {{ 42.55|round(1, 'floor') }} -> 42.5
请注意,即使舍入到 0 精度,也会返回一个浮点数。如果您需要一个真正的整数,请通过
int
进行管道传输{{ 42.55|round|int }} -> 43
- jinja-filters.select(value: 't.Iterable[V]', *args: Any, **kwargs: Any) 't.Iterator[V]' ¶
通过对每个对象应用测试来过滤对象序列,并且仅选择测试成功的对象。
如果未指定测试,则每个对象将被评估为布尔值。
示例用法
{{ numbers|select("odd") }} {{ numbers|select("odd") }} {{ numbers|select("divisibleby", 3) }} {{ numbers|select("lessthan", 42) }} {{ strings|select("equalto", "mystring") }}
类似于生成器解析,例如
(n for n in numbers if test_odd(n)) (n for n in numbers if test_divisibleby(n, 3))
变更日志
2.7 版本中已添加。
- jinja-filters.selectattr(value: 't.Iterable[V]', *args: Any, **kwargs: Any) 't.Iterator[V]' ¶
通过对每个对象的指定属性应用测试来过滤对象序列,并且仅选择测试成功的对象。
如果没有指定测试,则属性的值将被评估为布尔值。
示例用法
{{ users|selectattr("is_active") }} {{ users|selectattr("email", "none") }}
类似于生成器解析,例如
(u for user in users if user.is_active) (u for user in users if test_none(user.email))
变更日志
2.7 版本中已添加。
- jinja-filters.slice(value: 't.Collection[V]', slices: int, fill_with: 't.Optional[V]' = None) 't.Iterator[t.List[V]]' ¶
对迭代器进行切片,并返回包含这些项的列表列表。如果您想创建包含三个表示列的 ul 标记的 div,这将非常有用
<div class="columnwrapper"> {%- for column in items|slice(3) %} <ul class="column-{{ loop.index }}"> {%- for item in column %} <li>{{ item }}</li> {%- endfor %} </ul> {%- endfor %} </div>
如果你传递第二个参数,它将用于填充最后一次迭代中缺失的值。
- jinja-filters.sort(value: 't.Iterable[V]', reverse: bool = False, case_sensitive: bool = False, attribute: str | int | NoneType = None) 't.List[V]' ¶
使用 Python 的
sorted()
对一个可迭代对象进行排序。{% for city in cities|sort %} ... {% endfor %}
- 参数:
reverse – 降序排列,而不是升序排列。
case_sensitive – 对字符串进行排序时,将大写和小写字母分别进行排序。
attribute – 对对象或字典进行排序时,要按其排序的属性或键。可以使用点表示法,如
"address.city"
。可以是属性列表,如"age,name"
。
排序是稳定的,它不会改变比较相等元素的相对顺序。这使得按不同属性和顺序对排序进行链接成为可能。
{% for user in users|sort(attribute="name") |sort(reverse=true, attribute="age") %} ... {% endfor %}
作为对链接的快捷方式,当所有属性的方向都相同,传递一个以逗号分隔的属性列表。
{% for user in users|sort(attribute="age,name") %} ... {% endfor %}
变更日志
在 2.11.0 版本中更改:
attribute
参数可以是属性的逗号分隔列表,例如"age,name"
。在 2.6 版本中更改: 添加了
attribute
参数。
- jinja-filters.string(value)¶
如果一个对象不是字符串,则将其转换为字符串。这会保留一个
Markup
字符串,而不是将其转换回一个基本字符串,因此它仍将被标记为安全且不会再次转义。>>> value = escape("<User 1>") >>> value Markup('<User 1>') >>> escape(str(value)) Markup('&lt;User 1&gt;') >>> escape(soft_str(value)) Markup('<User 1>')
- jinja-filters.sum(iterable: 't.Iterable[V]', attribute: str | int | NoneType = None, start: V = 0) V ¶
返回数字序列的和加上参数“start”的值(默认为 0)。当序列为空时,它返回 start。
还可以仅对某些属性求和
Total: {{ items|sum(attribute='price') }}
变更日志
在 2.6 版中更改:添加了
attribute
参数以允许对属性求和。此外,start
参数已移至右侧。
- jinja-filters.tojson(value: Any, indent: int | None = None) markupsafe.Markup ¶
将对象序列化为 JSON 字符串,并标记为可在 HTML 中安全呈现。此过滤器仅用于 HTML 文档。
返回的字符串可以在 HTML 文档和
<script>
标记中安全呈现。例外情况是双引号引用的 HTML 属性;使用单引号或|forceescape
过滤器。- 参数:
value – 要序列化为 JSON 的对象。
indent – 传递给
dumps
的indent
参数,用于漂亮打印该值。
变更日志
在 2.9 版中添加。
- jinja-filters.truncate(s: str, length: int = 255, killwords: bool = False, end: str = '...', leeway: int | None = None) str ¶
返回字符串的截断副本。长度使用第一个参数指定,其默认为
255
。如果第二个参数为true
,则过滤器将在长度处截断文本。否则,它将丢弃最后一个单词。如果文本实际上已被截断,它将附加一个省略号 ("..."
)。如果你想要一个不同于"..."
的省略号,则可以使用第三个参数指定它。仅超出第四个参数中给出的容差范围的字符串不会被截断。{{ "foo bar baz qux"|truncate(9) }} -> "foo..." {{ "foo bar baz qux"|truncate(9, True) }} -> "foo ba..." {{ "foo bar baz qux"|truncate(11) }} -> "foo bar baz qux" {{ "foo bar baz qux"|truncate(11, False, '...', 0) }} -> "foo bar..."
较新 Jinja 版本的默认宽限期为 5,但之前为 0,但可以全局重新配置。
- jinja-filters.unique(value: 't.Iterable[V]', case_sensitive: bool = False, attribute: str | int | NoneType = None) 't.Iterator[V]' ¶
从给定的可迭代对象中返回一个唯一项列表。
{{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }} -> ['foo', 'bar', 'foobar']
唯一项按其在传递给过滤器的可迭代对象中的首次出现顺序生成。
- 参数:
case_sensitive – 将大小写字符串视为不同的字符串。
attribute – 使用此属性的唯一值过滤对象。
- jinja-filters.urlencode(value: str | Mapping[str, Any] | Iterable[Tuple[str, Any]]) str ¶
使用 UTF-8 对数据进行编码,以用于 URL 路径或查询。
当给定一个字符串时,围绕
urllib.parse.quote()
的基本包装,或者对于字典或可迭代对象,围绕urllib.parse.urlencode()
。- 参数:
value – 要引用的数据。字符串将被直接引用。字典或
(key, value)
对的可迭代对象将被连接为查询字符串。
当给定一个字符串时,“/”不会被引用。HTTP 服务器在路径中将 “/”和 “%2F”等效处理。如果您需要引用的斜杠,请使用
|replace("/", "%2F")
过滤器。变更日志
2.7 版本中已添加。
- jinja-filters.urlize(value: str, trim_url_limit: int | None = None, nofollow: bool = False, target: str | None = None, rel: str | None = None, extra_schemes: Iterable[str] | None = None) str ¶
将文本中的 URL 转换为可点击链接。
在某些情况下,这可能无法识别链接。通常,更全面的格式化程序(如 Markdown 库)是更好的选择。
适用于
http://
、https://
、www.
、mailto:
和电子邮件地址。识别带有尾随标点(句号、逗号、闭括号)和前导标点(开括号)的链接,但不包括标点。不识别包含标题字段的电子邮件地址(例如,mailto:address@example.com?cc=copy@example.com
)。- 参数:
value – 包含要链接的 URL 的原始文本。
trim_url_limit – 将显示的 URL 值缩短到此长度。
nofollow – 将
rel=nofollow
属性添加到链接。target – 将
target
属性添加到链接。rel – 将
rel
属性添加到链接。extra_schemes – 除了默认行为之外,识别以这些方案开头的 URL。默认为
env.policies["urlize.extra_schemes"]
,默认为没有额外方案。
变更日志
3.0 版中已更改: 添加了
extra_schemes
参数。3.0 版本中已更改: 为没有方案的 URL 生成
https://
链接。3.0 版本中已更改: 已更新解析规则。识别带有或不带有
mailto:
方案的电子邮件地址。验证 IP 地址。在更多情况下忽略括号和方括号。2.8 版本中已更改: 已添加
target
参数。
- jinja-filters.wordwrap(s: str, width: int = 79, break_long_words: bool = True, wrapstring: str | None = None, break_on_hyphens: bool = True) str ¶
将字符串换行到给定的宽度。将现有的换行符视为要单独换行的段落。
- 参数:
s – 要换行的原始文本。
width – 换行后的最大长度。
break_long_words – 如果单词长度大于
width
,则在多行显示。break_on_hyphens – 如果单词包含连字符,则可以在多行显示。
wrapstring – 连接每行换行文本的字符串。默认为
Environment.newline_sequence
。
变更日志
2.11 版中已更改: 现有换行符被视为单独换行的段落。
2.11 版中已更改: 添加
break_on_hyphens
参数。2.7 版中已更改: 添加
wrapstring
参数。
- jinja-filters.xmlattr(d: Mapping[str, Any], autospace: bool = True) str ¶
根据字典中的项创建 SGML/XML 属性字符串。
既不是
none
也不是undefined
的值会自动转义,安全地允许不受信任的用户输入。用户输入不应作为此过滤器的键。如果任何键包含空格、
/
斜线、>
大于号或=
等号,则会失败并显示ValueError
。无论如何,用户输入永远不应作为此过滤器的键,或者必须先单独验证。<ul{{ {'class': 'my_list', 'missing': none, 'id': 'list-%d'|format(variable)}|xmlattr }}> ... </ul>
结果类似于以下内容
<ul class="my_list" id="list-42"> ... </ul>
正如您所见,如果过滤器返回了内容,它会自动在项目前面添加一个空格,除非第二个参数为 false。
在版本 3.1.4 中更改: 不允许使用带有
/
斜杠、>
大于号或=
等号的键。在版本 3.1.3 中更改: 不允许使用带有空格的键。
内置测试列表¶
- jinja-tests.callable(obj, /)¶
返回对象是否可调用(即某种函数)。
请注意,类是可调用的,具有 __call__() 方法的类实例也是可调用的。
- jinja-tests.defined(value: Any) bool ¶
如果变量已定义,则返回 true
{% if variable is defined %} value of variable: {{ variable }} {% else %} variable is not defined {% endif %}
请参阅
default()
过滤器,了解设置未定义变量的简单方法。
- jinja-tests.eq(a, b, /)¶
与 a == b 相同。
- 别名:
==
,equalto
- jinja-tests.filter(value: str) bool ¶
按名称检查过滤器是否存在。当过滤器可能为可选时很有用。
{% if 'markdown' is filter %} {{ value | markdown }} {% else %} {{ value }} {% endif %}
变更日志
在版本 3.0 中添加。
- jinja-tests.ge(a, b, /)¶
与 a >= b 相同。
- 别名:
>=
- jinja-tests.gt(a, b, /)¶
与 a > b 相同。
- 别名:
>
,greaterthan
- jinja-tests.le(a, b, /)¶
与 a <= b 相同。
- 别名:
<=
- jinja-tests.lt(a, b, /)¶
与 a < b 相同。
- 别名:
<
,lessthan
- jinja-tests.ne(a, b, /)¶
与 a != b 相同。
- 别名:
!=
- jinja-tests.sameas(value: Any, other: Any) bool ¶
检查一个对象是否指向与另一个对象相同的内存地址
{% if foo.attribute is sameas false %} the foo attribute really is the `False` singleton {% endif %}
全局函数列表¶
以下函数默认情况下可在全局作用域中使用
- jinja-globals.range([start, ]stop[, step])¶
返回包含整数等差数列的列表。
range(i, j)
返回[i, i+1, i+2, ..., j-1]
;start (!) 默认为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。默认情况下,生成五个 HTML 段落,每个段落包含 20 到 100 个单词。如果 html 为 False,则返回常规文本。这对于生成用于布局测试的简单内容非常有用。
- jinja-globals.dict(\**items)¶
dict 字面量的便捷替代方案。
{'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 版中添加。
- 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' %}
其主要目的是允许将值从循环体内部传递到外部作用域。初始值可以作为字典、关键字参数或两者提供(与 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¶
如果启用了i18n 扩展,则可以将模板中的文本标记为可翻译。要将一个部分标记为可翻译,请使用 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 版中添加: 已添加 trimmed
和 notrimmed
修饰符。
如果翻译取决于消息出现的上下文,则 pgettext
和 npgettext
函数将 context
字符串作为第一个参数,该参数用于选择合适的翻译。要使用 {% trans %}
标记指定上下文,请在 trans
后将一个字符串作为第一个标记提供。
{% trans "fruit" %}apple{% endtrans %}
{% trans "fruit" trimmed count -%}
1 apple
{%- pluralize -%}
{{ count }} apples
{%- endtrans %}
在版本 3.1 中添加:可以将上下文传递给 trans
标记以使用 pgettext
和 npgettext
。
可以使用这些函数翻译表达式中的字符串
_(message)
:gettext
的别名。gettext(message)
:翻译消息。ngettext(singluar, plural, n)
:根据计数变量翻译单数或复数消息。pgettext(context, message)
:类似于gettext()
,但根据上下文字符串选择翻译。npgettext(context, singular, plural, n)
:类似于npgettext()
,但根据上下文字符串选择翻译。
可以像这样打印翻译后的字符串
{{ _("Hello, World!") }}
要使用占位符,请使用 format
过滤器。
{{ _("Hello, %(user)s!")|format(user=user.username) }}
始终对 format
使用关键字参数,因为其他语言可能不会按相同顺序使用这些单词。
如果激活了 新式 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') %}
循环控件¶
如果应用程序启用了 循环控件,则可以在循环中使用 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 %}
标记将可用,以转储当前上下文以及可用的过滤器和测试。这有助于了解模板中可以使用哪些内容,而无需设置调试器。
<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']}
With 语句¶
变更日志
在版本 2.3 中添加。
with 语句可以创建新的内部作用域。在此作用域内设置的变量在作用域外部不可见。
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 版本中,引用一个变量到另一个变量的行为会产生一些意外后果。特别是,一个变量可以引用在同一 with 块的开头语句中定义的另一个变量。这导致了清理后的作用域行为问题,并且此后得到了改进。特别是在较新的 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 之前)中,需要使用扩展启用此功能。现在已默认启用。
注释¶
要注释掉模板中一行的一部分,请使用注释语法,默认设置为
{# ... #}
。这对于注释掉模板的某些部分以进行调试或为其他模板设计人员或您自己添加信息非常有用