Jinja模板语法
本页面介绍Jinja模板引擎。虽然模板语言的详细描述可以在此处找到 Jinja文档,但本文提供了基本概念。
代码生成原则
代码生成是由一个小脚本驱动的,该脚本遍历域模型并使用Python Jinja模板语言写入文件。
鉴于生成器脚本已经读取并解析了IDL文件进入域模型,然后该后者作为模板语言内代码生成的根对象使用。以下是一个代码遍历域模型的示例
{% for module in system.modules %} {%- for interface in module.interfaces -%} SERVICE, {{module}}.{{interface}} {% endfor -%} {%- for struct in module.structs -%} STRUCT , {{module}}.{{struct}} {% endfor -%} {%- for enum in module.enums -%} ENUM , {{module}}.{{enum}} {% endfor -%} {% endfor %}
模板会遍历域对象,并将生成的文本写入文件。
语言结构
概要
模板包含变量和/或表达式,在模板渲染时用值替换;以及控制模板逻辑的标签。
有几个种类的定界符。Jinja的默认定界符配置如下
- {% ... %} 用于语句
- {{ ... }} 用于输出到模板输出的表达式
- {# ... #} 用于不包含在模板输出中的注释
- ## ... # 用于 行语句
控制结构
控制结构是指所有控制程序流程的东西 - 条件(即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.iteritems() %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %} </dl>
在for循环块内部有特殊变量可用
变量 | 描述 |
---|---|
loop.index | 循环当前迭代。 (从1开始) |
loop.index0 | 循环当前迭代。 (从0开始) |
loop.revindex | 从循环末尾到迭代的次数(从1开始) |
loop.revindex0 | 从循环末尾到迭代的次数(从0开始) |
loop.first | 如果是第一次迭代。 |
loop.last | 如果是最后一次迭代。 |
loop.length | 序列中的项目数。 |
请参阅更多Jinja 文档
与 Python 不同,在循环中无法使用 break 或 continue。然而,可以在迭代过程中过滤序列,从而跳过某些项。以下示例跳过了所有隐藏的用户。
{% for user in users if not user.hidden %} <li>{{ user.username|e }}</li> {% endfor %}
优点是特殊的循环变量将正确计数;因此不会计算未迭代的用户。
如果因为序列为空或过滤移除了序列中所有项目而没有进行迭代,可以使用 otherwise 来渲染默认块。
<ul> {% for user in users %} <li>{{ user.username|e }}</li> {% else %} <li><em>no users found</em></li> {% endfor %} </ul>
在 Python 中,else 块会在相应的循环没有 break 时执行。由于 Jinja 循环无法断开,因此选择了 else 关键字的稍有不同的行为。
还可以递归地使用循环。当处理递归数据(如站点地图或 RDFa)时,这非常有用。要递归地使用循环,基本方法是向循环定义添加递归修饰符,并在需要递归的地方使用新的可迭代对象调用循环变量。
以下示例实现了具有递归循环的站点地图。
<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 %} 来重新绑定变量 loop。然后,我们可以使用 {{ outer_loop(...) }} 来调用它。
请注意,循环中的赋值将在迭代结束时清除,并且不能超出循环的作用域。Jinja2 的早期版本在某种情况下似乎能够工作,但这不被支持。
如果
Jinja 中的 if 语句与 Python 中的 if 语句类似。在基本形式中,可以用来测试变量是否已定义、不为空且不为假。
{% if users %} <ul> {% for user in users %} <li>{{ user.username|e }}</li> {% endfor %} </ul> {% endif %}
对于多个分支,elif 和 else 可以像在 Python 中一样使用。也可以在那里使用更复杂的表达式。
{% if kenny.sick %} Kenny is sick. {% elif kenny.dead %} You killed Kenny! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}
测试
除了过滤器之外,还有所谓的“测试”可用。测试可用于测试变量是否与常见表达式匹配。要测试变量或表达式,其名称后面跟测试名称。例如,要找出变量是否已定义,可以尝试 name is defined,它将返回 true 或 false,具体取决于 name 是否在当前模板上下文中定义。
测试也可以接受参数。如果测试只接受一个参数,则可以省略括号。例如,以下两个表达式执行相同操作。
{% if loop.index is divisibleby 3 %} {% if loop.index is divisibleby(3) %}
可以在Jinja 文档页面上找到内置测试列表。
过滤器
变量可以通过称为过滤器的高级函数进行修改。过滤器通过管道符号(|)与变量分开,并可能在括号中具有可选参数。可以链式使用多个过滤器。一个过滤器的输出应用于下一个。
例如,{{ name|striptags|title }} 将从变量 name 中删除所有 HTML 标签,并将输出转换为标题格式(title(striptags(name)))。
接受参数的过滤器具有括号包围的参数,就像函数调用一样。例如:{{ listx|join(', ') }}
将列表连接为逗号分隔的字符串(等同于 str.join(', ', listx)
)。不过,始终是传递给过滤函数的第一个参数。
可以通过将 Python 函数作为新过滤器注册到环境对象中来自定义过滤器。
def lower_first_filter(s): s = str(s) return s[0].lower() + s[1:] env = Environment( loader=FileSystemLoader(search_path), trim_blocks=True, lstrip_blocks=True ) env.filters['lowerfirst'] = lower_first_filter
然后,名为lower的第一个过滤器将从模板中可用
{{ var | lowerfirst }}
所有支持的过滤器的列表可以在过滤器参考中找到。
变量
模板变量由传递给模板的上下文字典定义。变量可以是具有自身属性的复杂对象。
除了标准的Python __getitem__ “切片”语法 ([]),还可以使用点 (.) 来访问变量的属性。
以下行是等价的
{{ foo.bar }} {{ foo['bar'] }}
如果变量或属性不存在,其值将为未定义值。该类型值的解释取决于应用程序的配置:默认行为是在打印或迭代时评估为空字符串,并在其他操作中失败。
注释
要在模板中注释掉一行的一部分,请使用默认设置的字面语法 {# ... #}。这在调试模板或为其他模板设计者或自己添加信息时很有用
{# note: commented-out template because we no longer use this {% for user in users %} ... {% endfor %} #}
行语句
如果应用程序启用了行语句,则可以将一行标记为语句。例如,如果行语句的前缀配置为 #,以下两个示例是等价的
<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
赋值
在代码块内部,您还可以将值赋给变量。顶级赋值(在块、宏或循环之外)像顶级宏一样从模板中导出,可以被其他模板导入。
赋值使用set标签,并且可以有多个目标
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %} {% set key, value = call_something() %}
不可能在块内设置变量并使它们在块外显示。这也适用于循环。该规则的唯一例外是if语句,它不会引入作用域。
空白控制
默认配置中
- 如果有单行尾换行符,则将其删除
- 其他空白(空格、制表符、换行符等)将被返回为不变
如果应用程序配置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>
在模板中也可以手动去除空白字符。在块(例如For标签)的开头或结尾、注释或变量表达式前加一个负号(-)时,将去除该块前后或此块的空白字符。
{% for item in seq -%} {{ item }} {%- endfor %}
这将产生所有之间没有空白的元素。如果seq是一个从1到9的数字列表,输出将是123456789。
如果启用了行语句,它们将自动去除行首的空白。
默认情况下,Jinja2也会移除尾随的换行符。为了保留单个尾随换行符,请配置Jinja以使用keep_trailing_newline。
请不要在标签和负号之间添加空格。
有效
{%- if foo -%}...{% endif %}
无效
{% - if foo - %}...{% endif %}
©版权所有 © 2020 The Qt Company Ltd。此处包含的文档贡献权属于各自的拥有者。本提供的文档是在自由软件基金会发布的GNU自由文档许可版本1.3的条款下提供的。Qt和相应的商标是芬兰的The Qt Company Ltd. 和/或其他国家的商标。所有其他商标均为其各自所有者的财产。