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 中是否包含特定字符串,从而验证条件分支和循环逻辑的正确性。
Jinja2 控制结构在报表模板中的组合流程 动态数据传入 {% if data %} 条件判断:数据是否存在 {% else %} 无数据提示 {% for item in data %} {{ render_row(item) }} 宏定义:复用单元格渲染 {% endfor %} 渲染完成 HTML 条件 + 循环 + 宏 三层组合