4、Jinja2 模板控制结构:条件判断、循环与宏定义
Jinja2 模板引擎的核心能力之一在于其丰富的控制结构,使得静态模板能够根据动态数据生成差异化的报表内容。本章聚焦于三大控制结构:条件判断(if/elif/else)、循环(for) 以及 宏定义与调用。掌握这些结构是编写可维护、可复用的报表模板的基础,也是接口验证中判断模板逻辑正确性的关键。
4.1 条件判断:if/elif/else
条件判断用于根据变量或表达式的值,动态选择渲染哪一段模板内容。在报表导出中,常用于处理空数据、状态标识、权限控制等场景。
4.1.1 基本语法
{% if condition %}
渲染内容 A
{% elif another_condition %}
渲染内容 B
{% else %}
渲染内容 C
{% endif %}
- condition:可以是变量、比较表达式(
==,!=,>,<)、逻辑运算(and,or,not)或布尔测试。 - 空值判断:
{% if data %}在data为None、空列表、空字典、空字符串或False时视为假。 - elif 与 else:均为可选,
elif可重复多次。
4.1.2 报表测试中的典型场景
| 场景 | 模板代码示例 | 验证要点 |
|---|---|---|
| 空数据提示 | {% if rows %}{% else %}<p>暂无数据</p>{% endif %} |
传入空列表时,页面应显示提示而非空白或报错。 |
| 状态标签着色 | {% if status == 'success' %}绿色{% elif status == 'fail' %}红色{% else %}灰色{% endif %} |
每种状态对应的颜色/文案是否正确渲染。 |
| 权限控制 | {% if user.role == 'admin' %}显示管理按钮{% endif %} |
非 admin 用户不应看到管理按钮。 |
测试提示: 条件判断的边界测试应覆盖:
None、空字符串、0、False、空列表、空字典,以及预期为真的正常值。特别注意 0 在 Jinja2 中视为假,与 Python 一致。
4.2 循环:for 遍历
for 循环用于遍历列表、字典或可迭代对象,是生成表格行、列表项、图表数据点的核心结构。
4.2.1 基本语法与内置变量
{% for item in items %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ item.name }}</td>
</tr>
{% else %}
<tr><td colspan="2">无数据</td></tr>
{% endfor %}
Jinja2 为 for 循环提供了丰富的 内置变量,便于在循环体内获取索引、奇偶性、首尾状态等:
| 变量名 | 说明 | 示例值 |
|---|---|---|
loop.index |
当前迭代次数(从 1 开始) | 1, 2, 3 |
loop.index0 |
当前迭代次数(从 0 开始) | 0, 1, 2 |
loop.first |
是否为第一次迭代 | True / False |
loop.last |
是否为最后一次迭代 | True / False |
loop.length |
序列的总长度 | 10 |
loop.cycle |
在指定值之间循环切换 | loop.cycle('odd','even') |
4.2.2 遍历字典
{% for key, value in report_meta.items() %}
<p>{{ key }}: {{ value }}</p>
{% endfor %}
常见陷阱: 在
for 循环中修改被遍历的列表(如 items.append())可能导致不可预期的行为。测试时应确保模板不修改原始数据集合。
4.3 宏定义与调用
宏(Macro)是 Jinja2 中实现模板复用的核心机制,类似于编程语言中的函数。宏允许你将一段重复使用的模板片段封装起来,通过参数传递数据,减少重复代码。
4.3.1 定义与调用宏
{% macro render_cell(value, align='left') %}
<td style="text-align: {{ align }};">{{ value }}</td>
{% endmacro %}
<tr>
{{ render_cell('张三') }}
{{ render_cell(98.5, 'center') }}
</tr>
- 定义位置:宏通常定义在模板顶部或单独的
_macros.html文件中,通过{% import 'macros.html' as macros %}导入。 - 默认参数:支持默认值,如
align='left'。 - 可变参数:使用
*varargs和**kwargs接收不定长参数。
4.3.2 宏的导入与作用域
{% from 'shared/report_macros.html' import render_table_header, render_row with context %}
with context 关键字会将当前模板的上下文变量传递给宏,否则宏只能访问显式传入的参数。
| 导入方式 | 语法 | 适用场景 |
|---|---|---|
| import 整个文件 | {% import 'macros.html' as m %} |
调用多个宏时,使用 m.macro_name() |
| from import 特定宏 | {% from 'macros.html' import foo %} |
只需少量宏,直接使用 foo() |
| 带上下文导入 | {% from 'macros.html' import bar with context %} |
宏内部需要访问模板全局变量(如 request) |
测试策略: 宏测试应关注:参数默认值是否生效、可变参数是否被正确传递、
with context 是否引入了预期外的变量、宏嵌套调用时作用域是否隔离。
4.4 控制结构组合:条件 + 循环 + 宏
实际报表模板中,三种结构经常组合使用。以下是一个综合示例:
{% macro render_status_badge(status) %}
{% if status == 'active' %}
<span class="badge badge-success">启用</span>
{% elif status == 'inactive' %}
<span class="badge badge-secondary">停用</span>
{% else %}
<span class="badge badge-warning">未知</span>
{% endif %}
{% endmacro %}
<table>
<thead>
<tr><th>序号</th><th>名称</th><th>状态</th></tr>
</thead>
<tbody>
{% for user in user_list %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ user.name }}</td>
<td>{{ render_status_badge(user.status) }}</td>
</tr>
{% else %}
<tr><td colspan="3">暂无用户数据</td></tr>
{% endfor %}
</tbody>
</table>
该模板在 for 循环中调用宏,宏内部使用 if/elif/else 进行条件渲染,同时利用 loop.index 生成序号,for...else 处理空数据场景。
4.5 控制结构测试要点总结
| 控制结构 | 测试项 | 预期结果 |
|---|---|---|
| if/elif/else | 所有分支覆盖(真/假/边界值) | 每个分支渲染正确的内容 |
| for 循环 | 空列表、单元素、大量元素 | 循环次数正确,loop 变量值准确 |
| for...else | 空列表时执行 else 块 | 显示无数据提示,不报错 |
| 宏定义 | 默认参数、位置参数、关键字参数 | 渲染结果与预期一致 |
| 宏导入 | import vs from import vs with context |
作用域正确,无变量泄漏 |
接口验证建议: 在接口测试中,针对模板控制结构的验证,建议构造多组数据驱动用例,覆盖每个分支和边界条件。使用
assertIn / assertNotIn 检查渲染后的 HTML 中是否包含特定字符串,从而验证条件分支和循环逻辑的正确性。