Jinja 不只用于 Web:用模板生成动态文本文件

2026-09-13 31 预计阅读时间: 1 分钟
来源: realpython.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

Jinja 常被认为是 Web 应用中的 HTML 模板引擎,但它的能力并不依赖某个 Web 框架。只要程序需要根据变量、条件和循环生成文本,Jinja 就能把固定格式与动态内容分开管理。

这意味着它不仅适合渲染页面,也适合生成配置文件、邮件正文、代码片段、部署清单以及其他结构稳定、内容变化的文本文件。

把模板和数据分开

直接在 Python 字符串中拼接内容,通常很快会变得难以阅读:变量、条件、循环和格式控制混在业务代码里,修改文本格式时还可能影响程序逻辑。

Jinja 的基本思路是把文本写在独立的模板文件中,再由 Python 提供数据。例如,模板可以这样写:

Hello, {{ user.name }}!

{% if user.active %}
Your account is active.
{% else %}
Your account is waiting for activation.
{% endif %}

Your projects:
{% for project in projects %}
- {{ project }}
{% endfor %}

{{ ... }} 用于输出表达式,{% ... %} 用于控制流程。模板仍然接近最终文本的样子,因此产品文案或配置格式变化时,不必在 Python 代码中寻找大量字符串拼接逻辑。

一个可运行的独立示例

下面的例子不依赖 Flask、Django 或其他 Web 框架。它读取一个模板文件,根据 Python 字典渲染内容,并将结果写入文本文件。

先安装 Jinja:

python -m pip install Jinja2

创建目录和模板文件:

mkdir -p jinja-demo/templates

将下面内容保存为 jinja-demo/templates/report.txt.j2

Project report: {{ project.name }}
Owner: {{ project.owner }}
Status: {{ project.status }}

Tasks:
{% for task in project.tasks %}
- [{{ "x" if task.done else " " }}] {{ task.title }}
{% endfor %}

再将下面内容保存为 jinja-demo/render.py

from pathlib import Path

from jinja2 import Environment, FileSystemLoader, StrictUndefined


base_dir = Path(__file__).parent

env = Environment(
    loader=FileSystemLoader(base_dir / "templates"),
    undefined=StrictUndefined,
    trim_blocks=True,
    lstrip_blocks=True,
)

template = env.get_template("report.txt.j2")

project = {
    "name": "Inventory API",
    "owner": "Platform Team",
    "status": "in progress",
    "tasks": [
        {"title": "Define the API contract", "done": True},
        {"title": "Add integration tests", "done": False},
    ],
}

output = template.render(project=project)
output_path = base_dir / "report.txt"
output_path.write_text(output, encoding="utf-8")

print(f"Generated: {output_path}")

运行:

cd jinja-demo
python render.py
cat report.txt

输出文件会根据 project 数据生成。把 project 换成从 JSON、数据库或命令行参数读取的数据,就可以将同一套模板用于不同输入。

StrictUndefined 值得在生成配置或部署文件时启用。模板引用了不存在的变量时,程序会直接报错,而不是静默生成带有空字段的文件。这样可以更早发现拼写错误和不完整的数据。

让模板适合长期维护

当模板数量增加时,可以使用继承和宏减少重复内容。例如,多个 HTML 页面可以共享基础模板:

{# base.html #}
<!doctype html>
<html lang="en">
  <head>
    <title>{% block title %}Application{% endblock %}</title>
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>

页面模板只需要填充变化部分:

{% extends "base.html" %}

{% block title %}Dashboard{% endblock %}

{% block content %}
<h1>Dashboard</h1>
<p>Welcome, {{ user.name }}.</p>
{% endblock %}

对于重复的输出片段,可以进一步抽成宏。对于配置文件、邮件和代码生成,也建议保持模板职责单一:一个模板负责一种输出格式,数据准备和业务计算放在 Python 代码中完成。

还需要注意自动转义。生成 HTML 时,合适的环境配置可以降低未转义内容带来的风险;生成纯文本、INI、Shell 或其他配置格式时,则应根据目标格式处理转义规则,不能假设所有文本都可以直接插入。

采用前的检查清单

Jinja 适合以下场景:输出格式相对稳定,但内容来自变量、列表或条件;模板需要由非核心业务代码的维护者阅读;同一格式要根据多组数据重复生成。

实践时可以遵循几条边界:

  • 模板负责展示和文本结构,不负责复杂业务计算。
  • 对关键输出启用 StrictUndefined,并为模板数据建立明确结构。
  • 生成文件后执行格式校验、语法检查或快照测试。
  • 处理 HTML、Shell、SQL 和配置文件时,分别评估转义与注入风险。
  • 模板和数据都来自不可信来源时,不要直接暴露高权限对象或任意函数。

Jinja 的价值不在于把所有文本都改写成模板,而在于为“固定结构加动态内容”的任务提供清晰边界。即使没有 Web 框架,它也可以成为一个轻量、可测试、易维护的文本生成组件。


相关推荐