4. Jinja2模板引擎:模板语法基础、变量与过滤器、控制结构(if/for)、模板继承与宏

好,咱们今天聊聊Jinja2。说实话,在搭建法律文书自动生成系统时,Jinja2是我最依赖的伙伴之一。它就像文书的「骨架」,把数据和格式完美地粘合在一起。你想想看,一份判决书,事实认定、法律依据、判决结果都是动态的,但结构是固定的——Jinja2就是干这个的。

4.1 模板语法基础

Jinja2的语法,说白了就三种「标记」:

  • 变量:用 {{ ... }} 包裹,比如 {{ 当事人姓名 }}
  • 语句:用 {% ... %} 包裹,比如 {% if 性别 == '男' %}
  • 注释:用 {# ... #} 包裹,不会出现在最终文档里

我刚开始用的时候,老把变量和语句搞混。后来记住一个诀窍:变量是「输出」东西,语句是「做」事情。嗯,这样就好记多了。

{# 这是一个典型的法律文书模板片段 #}
{{ 法院名称 }}
{{ 案号 }}

当事人:{{ 原告姓名 }}({{ 原告性别 }})
我的小习惯:变量名尽量用中文拼音或英文,别用缩写。比如 dangshiren_xingmingdsrxm 好维护得多。我在项目里吃过这个亏,后来重构时改得想哭。

4.2 变量与过滤器

变量就是数据,过滤器就是「加工」数据的工具。Jinja2内置了很多好用的过滤器,我挑几个法律文书里常用的说说:

过滤器 作用 示例
default 设置默认值 {{ 被告姓名 | default('未知') }}
upper 转大写 {{ 法院名称 | upper }}
date 格式化日期 {{ 立案日期 | date('Y年m月d日') }}
join 拼接列表 {{ 证据清单 | join('、') }}

过滤器可以链式调用,比如:{{ 当事人姓名 | default('未知') | upper }}。不过我个人建议别链太多,超过两个就有点难读了。

避坑指南:我曾经在生成判决书时,忘记给日期字段加 default 过滤器。结果有一条记录立案日期是空的,模板直接报错,整个文书生成流程卡住了。从那以后,所有可能为空的字段我都加上了默认值。

4.3 控制结构:if/for

控制结构是模板的「大脑」。没有它,模板就是死板的填空。有了它,模板才能「思考」。

4.3.1 if 条件判断

法律文书里经常需要根据条件输出不同内容。比如:

{% if 判决结果 == '支持' %}
  本院予以支持。
{% elif 判决结果 == '部分支持' %}
  本院予以部分支持。
{% else %}
  本院不予支持。
{% endif %}

你想想看,如果没有if,你得写三个不同的模板,多麻烦。Jinja2一个模板就搞定了。

4.3.2 for 循环

法律文书里经常有「证据清单」、「法律依据列表」这种重复结构。for循环就是干这个的:

证据清单:
{% for 证据 in 证据列表 %}
  {{ loop.index }}. {{ 证据.名称 }}({{ 证据.类型 }})
{% endfor %}

这里有个小细节:loop.index 是Jinja2内置的循环变量,从1开始计数。还有 loop.firstloop.last 可以判断首尾元素。我在生成「合议庭成员」列表时经常用 loop.last 来控制逗号和「和」字的输出。

实战经验:有一次我需要生成一份包含「原告证据」和「被告证据」的质证意见。我用嵌套for循环,外层遍历当事人,内层遍历证据。代码看起来复杂,但实际运行效果非常好。关键是保持缩进清晰,不然自己都看不懂。

4.4 模板继承与宏

这部分是我觉得Jinja2最强大的地方。说白了,就是「复用」。

4.4.1 模板继承

法律文书有很多共同结构:页眉、页脚、法院名称、案号格式。你难道要每个模板都写一遍?当然不。

先写一个「基模板」:

<!-- base.html -->
<div class="header">
  {{ 法院名称 }}
  <hr>
</div>

{% block 正文 %}{% endblock %}

<div class="footer">
  本判决书一式{{ 份数 }}份
{% endblock %}

然后「子模板」继承它:

{% extends "base.html" %}

{% block 正文 %}
  <p>原告:{{ 原告姓名 }}</p>
  <p>被告:{{ 被告姓名 }}</p>
  <p>案由:{{ 案由 }}</p>
{% endblock %}

这样,页眉页脚只需要维护一份。我见过有人把20多份文书模板都写成独立的,结果法院名称改了,要改20个文件。用继承,改一个就行。

4.4.2 宏

宏就像函数,把重复的HTML片段封装起来。比如法律文书里经常要写「当事人信息」:

{% macro 当事人信息(姓名, 性别, 出生日期, 住址) %}
  <p>姓名:{{ 姓名 }}</p>
  <p>性别:{{ 性别 }}</p>
  <p>出生日期:{{ 出生日期 | date('Y年m月d日') }}</p>
  <p>住址:{{ 住址 }}</p>
{% endmacro %}

使用时:

{{ 当事人信息(原告姓名, 原告性别, 原告出生日期, 原告住址) }}

嗯,这里要注意:宏的参数顺序要固定,不然容易传错。我建议用关键字参数,虽然写起来长一点,但可读性好很多。

我的建议:把常用的宏放在一个单独的文件里,比如 macros.html,然后在需要的地方用 {% import "macros.html" as 宏 %} 导入。这样模板文件会非常干净。

4.5 知识体系结构图

下面这张图是我梳理的Jinja2模板引擎核心知识体系,你可以对照着看:

Jinja2 模板引擎 语法基础 {{ 变量 }} {% 语句 %} {# 注释 #} 变量与过滤器 default / upper / date join / length / lower 链式调用:| filter1 | filter2 控制结构 if / elif / else / endif for / endfor / loop.index loop.first / loop.last 模板继承与宏 {% extends "base.html" %} {% macro 名称(参数) %}

这张图把Jinja2的四个核心模块串起来了。你从中心出发,沿着分支走一遍,基本就能掌握全貌。

总结一下:Jinja2模板引擎的核心就是「变量输出 + 逻辑控制 + 复用机制」。变量和过滤器解决「数据怎么显示」,控制结构解决「什么时候显示什么」,模板继承和宏解决「怎么少写重复代码」。这三板斧用好了,法律文书自动生成系统的模板部分就稳了。

公众号:蓝海资料掘金营,微信deep3321