控制结构清单
最后更新于:2022-04-01 04:04:08
控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/ekse )、 for 循环、以及宏和块之类的东西。控制结构在默认语法中以 {% .. %} 块的形式 出现。
### For[](http://docs.jinkan.org/docs/jinja2/templates.html#for "Permalink to this headline")
遍历序列中的每项。例如,要显示一个由 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>
~~~
注意无论如何字典通常是无序的,所以你可能需要把它作为一个已排序的列表传入 到模板或使用 dictsort 过滤器。
在一个 for 循环块中你可以访问这些特殊的变量:
| 变量 | 描述 |
| --- | --- |
| loop.index | 当前循环迭代的次数(从 1 开始) |
| loop.index0 | 当前循环迭代的次数(从 0 开始) |
| loop.revindex | 到循环结束需要迭代的次数(从 1 开始) |
| loop.revindex0 | 到循环结束需要迭代的次数(从 0 开始) |
| loop.first | 如果是第一次迭代,为 True 。 |
| loop.last | 如果是最后一次迭代,为 True 。 |
| loop.length | 序列中的项目数。 |
| loop.cycle | 在一串序列间期取值的辅助函数。见下面的解释。 |
在 for 循环中,可以使用特殊的 loop.cycle 辅助函数,伴随循环在一个字符串/变 量列表中周期取值:
~~~
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
~~~
从 Jinja 2.1 开始,一个额外的 cycle 辅助函数允许循环限定外的周期取值。 更多信息请阅读 [*全局函数清单*](http://docs.jinkan.org/docs/jinja2/templates.html#builtin-globals) 。
与 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>
~~~
也可以递归地使用循环。当你处理诸如站点地图之类的递归数据时很有用。要递归地 使用循环,你只需要在循环定义中加上 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>
~~~
### If[](http://docs.jinkan.org/docs/jinja2/templates.html#if "Permalink to this headline")
Jinja 中的 if 语句可比 Python 中的 if 语句。在最简单的形式中,你可以测试 一个变量是否未定义,为空或 false:
~~~
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
~~~
像在 Python 中一样,用 elif 和 else 来构建多个分支。你也可以用更复杂的 [*表达式*](http://docs.jinkan.org/docs/jinja2/templates.html#expressions):
~~~
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
~~~
If 也可以被用作 [*内联表达式*](http://docs.jinkan.org/docs/jinja2/templates.html#if-expression) 并作为 [*循环过滤*](http://docs.jinkan.org/docs/jinja2/templates.html#loop-filtering) 。
### 宏[](http://docs.jinkan.org/docs/jinja2/templates.html#id19 "Permalink to this headline")
宏类似常规编程语言中的函数。它们用于把常用行为作为可重用的函数,取代 手动重复的工作。
这里是一个宏渲染表单元素的小例子:
~~~
{% 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*](http://docs.jinkan.org/docs/jinja2/templates.html#import) 。
在宏内部,你可以访问三个特殊的变量:
varargs
如果有多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在 varargs变量上。
kwargs
同 varargs ,但只针对关键字参数。所有未使用的关键字参数会存储在 这个特殊变量中。
caller
如果宏通过 [*call*](http://docs.jinkan.org/docs/jinja2/templates.html#call) 标签调用,调用者会作为可调用的宏被存储在这个 变量中。
宏也可以暴露某些内部细节。下面的宏对象属性是可用的:
name
宏的名称。 {{ input.name }} 会打印 input 。
arguments
一个宏接受的参数名的元组。
defaults
默认值的元组。
catch_kwargs
如果宏接受额外的关键字参数(也就是访问特殊的 kwargs 变量),为 true 。
catch_varargs
如果宏接受额外的位置参数(也就是访问特殊的 varargs 变量),为 true 。
caller
如果宏访问特殊的 caller 变量且由 [*call*](http://docs.jinkan.org/docs/jinja2/templates.html#call) 标签调用,为 true 。
如果一个宏的名称以下划线开始,它不是导出的且不能被导入。
### 调用[](http://docs.jinkan.org/docs/jinja2/templates.html#call "Permalink to this headline")
在某些情况下,需要把一个宏传递到另一个宏。为此,可以使用特殊的 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 %}
~~~
### 过滤器[](http://docs.jinkan.org/docs/jinja2/templates.html#id21 "Permalink to this headline")
过滤器段允许你在一块模板数据上应用常规 Jinja2 过滤器。只需要把代码用 filter 节包裹起来:
~~~
{% filter upper %}
This text becomes uppercase
{% endfilter %}
~~~
### 赋值[](http://docs.jinkan.org/docs/jinja2/templates.html#id22 "Permalink to this headline")
在代码块中,你也可以为变量赋值。在顶层的(块、宏、循环之外)赋值是可导出的,即 可以从别的模板中导入。
赋值使用 set 标签,并且可以为多个变量赋值:
~~~
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}
~~~
### 继承[](http://docs.jinkan.org/docs/jinja2/templates.html#id23 "Permalink to this headline")
extends 标签用于从另一个模板继承。你可以在一个文件中使用多次继承,但是 只会执行其中的一个。见上面的关于 [*模板继承*](http://docs.jinkan.org/docs/jinja2/templates.html#template-inheritance) 的节。
### 块[](http://docs.jinkan.org/docs/jinja2/templates.html#id24 "Permalink to this headline")
块用于继承,同时作为占位符和用于替换的内容。 [*模板继承*](http://docs.jinkan.org/docs/jinja2/templates.html#template-inheritance) 节中详细地介绍了块。
### 包含[](http://docs.jinkan.org/docs/jinja2/templates.html#id25 "Permalink to this headline")
include 语句用于包含一个模板,并在当前命名空间中返回那个文件的内容渲 染结果:
~~~
{% include 'header.html' %}
Body
{% include 'footer.html' %}
~~~
被包含的模板默认可以访问活动的上下文中的变量。更多关于导入和包含的上下文 行为见[*导入上下文行为*](http://docs.jinkan.org/docs/jinja2/templates.html#import-visibility) 。
从 Jinja 2.2 开始,你可以把一句 include 用 ignore missing 标记,这样 如果模板不存在,Jinja 会忽略这条语句。当与 with 或 without context 语句联合使用时,它必须被放在上下文可见性语句 *之前* 。这里是一些有效的例 子:
~~~
{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
~~~
New in version 2.2.
你也可以提供一个模板列表,它会在包含前被检查是否存在。第一个存在的模板会 被包含进来。如果给出了 ignore missing ,且所有这些模板都不存在,会退化 至不做任何渲染,否则将会抛出一个异常。
例子:
~~~
{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
~~~
Changed in version 2.4: 如果传递一个模板对象到模板上下文,你可以用 include 包含这个对 象。
### 导入[](http://docs.jinkan.org/docs/jinja2/templates.html#import "Permalink to this headline")
Jinja2 支持在宏中放置经常使用的代码。这些宏可以被导入,并不同的模板中使用。这 与 Python 中的 import 语句类似。要知道的是,导入量会被缓存,并且默认下导入的 模板不能访问当前模板中的非全局变量。更多关于导入和包含的上下文行为见 [*导入上下文行为*](http://docs.jinkan.org/docs/jinja2/templates.html#import-visibility) 。
有两种方式来导入模板。你可以把整个模板导入到一个变量或从其中导入请求特定的宏 /导出量。
比如我们有一个渲染表单(名为 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: 如果传递一个模板对象到模板上下文,从那个对象中导入。