1. 什么是 Jinja2? #
- Jinja2 是 Python 最流行的模板引擎,用来把「模板 + 数据」渲染成最终文本(HTML、邮件、配置文件等)。
- 模板里写占位符和简单逻辑,Python 代码里传入变量,调用
render()得到结果。 - Flask 默认用 Jinja2 渲染 HTML;Django 有自己的模板系统,但也可选用 Jinja2。
- 典型场景:Flask 网页、邮件正文、批量生成报告 HTML。
- 核心公式:模板 + 数据字典 = 输出字符串。
2. 模板语法 #
- Jinja2 模板由三种定界符组成,分别负责注释、输出变量和执行逻辑。
- 语法接近 Python,但模板里不能写任意 Python 代码(如
import os)。 - 日常开发 90% 的时间只用:
{{ 变量 }}、{% if %}、{% for %}和过滤器。
2.1 三种定界符 #
| 写法 | 作用 | 示例 |
|---|---|---|
{# ... #} |
注释,不输出 | {# 这是注释 #} |
{{ ... }} |
输出变量或表达式 | {{ user.name }} |
{% ... %} |
控制语句(if/for/extends 等) | {% if ok %}...{% endif %} |
2.2 变量与常用过滤器 #
- 变量由 Python 的
render()传入,模板里用{{ 变量名 }}输出。 - 支持点号访问属性(
user.name)和方括号访问(users[0]、config['key'])。 - 过滤器用管道符
|连接,写法:{{ 变量|过滤器名 }},可链式叠加。
| 过滤器 | 作用 | 示例 | |
|---|---|---|---|
upper / lower |
大小写转换 | `{{ name\ | upper }}` |
default |
值为空时用默认值 | `{{ name\ | default('匿名') }}` |
length |
取长度 | `{{ users\ | length }}` |
join |
列表拼成字符串 | `{{ tags\ | join(', ') }}` |
e / escape |
HTML 转义 | `{{ text\ | e }}` |
safe |
标记为安全 HTML(慎用) | `{{ html\ | safe }}` |
- 链式示例:
{{ name|trim|lower|default('guest') }}
2.3 条件判断 #
- 用
{% if %}/{% elif %}/{% else %}/{% endif %}做条件渲染。 - 条件表达式写法与 Python 类似。
{# 说明:若 score 大于等于 90,输出「优秀」 #}
{% if score >= 90 %}
优秀
{# 说明:否则若 score 大于等于 60,输出「及格」 #}
{% elif score >= 60 %}
及格
{# 说明:以上都不满足时输出「不及格」 #}
{% else %}
不及格
{# 说明:结束 if 语句块 #}
{% endif %}2.4 循环 #
- 用
{% for item in items %}/{% endfor %}遍历列表、字典等。 - 循环内可用
loop变量获取序号和首尾状态。
loop 属性 |
含义 |
|---|---|
loop.index |
当前序号(从 1 开始) |
loop.first |
是否第一个元素 |
loop.last |
是否最后一个元素 |
loop.length |
总元素数 |
{# 说明:开始无序列表 #}
<ul>
{# 说明:遍历 users 列表中的每个 user #}
{% for user in users %}
{# 说明:输出序号和用户名 #}
<li>{{ loop.index }}. {{ user.name }}</li>
{# 说明:若 users 为空,执行 else 分支 #}
{% else %}
<li>暂无数据</li>
{# 说明:结束 for 循环 #}
{% endfor %}
</ul>{% for %}...{% else %}中的else在列表为空时执行,不是循环结束后执行。{% set 变量 = 值 %}可在模板内定义临时变量。
3. 基本使用 #
- Python 侧有两种常见方式:直接用字符串创建模板,或从
templates/文件夹加载。 - 推荐项目里使用
Environment+FileSystemLoader,便于管理多个模板文件。 - Jinja2 3.x 渲染 HTML 时,应开启
autoescape防止 XSS(Flask 会自动开启)。
3.1 字符串模板(适合简单场景) #
# 说明:从 jinja2 导入 Template 类,用于从字符串创建模板
from jinja2 import Template
# 说明:用字符串创建模板,{{ name }} 是占位符
template = Template("你好,{{ name }}!")
# 说明:传入 name 变量并渲染模板
result = template.render(name="小明")
# 说明:打印渲染结果
print(result)3.2 从文件加载(项目常用) #
- 模板文件通常放在项目根目录的
templates/文件夹下。 get_template('xxx.html')加载文件,render(**变量)传入数据。
# 说明:导入 Environment 环境类
# 说明:导入 FileSystemLoader,从文件夹加载模板
# 说明:导入 select_autoescape,按文件类型自动转义 HTML
from jinja2 import Environment, FileSystemLoader, select_autoescape
# 说明:创建模板环境,指定 templates 目录并开启 HTML 自动转义
env = Environment(
loader=FileSystemLoader("templates"),
autoescape=select_autoescape(["html", "xml"]),
)
# 说明:加载 user_list.html 模板文件
template = env.get_template("user_list.html")
# 说明:传入变量并渲染为 HTML 字符串
html = template.render(title="用户列表", users=["Alice", "Bob"])
# 说明:打印渲染结果
print(html)templates/user_list.html:
{# 说明:HTML 文档类型声明 #}
<!DOCTYPE html>
{# 说明:html 根元素 #}
<html>
{# 说明:页面头部 #}
<head>
{# 说明:输出 title 变量作为页面标题 #}
<title>{{ title }}</title>
</head>
{# 说明:页面主体 #}
<body>
{# 说明:输出一级标题 #}
<h1>{{ title }}</h1>
{# 说明:开始无序列表 #}
<ul>
{# 说明:遍历 users 列表 #}
{% for user in users %}
{# 说明:输出每个用户名 #}
<li>{{ user }}</li>
{# 说明:结束 for 循环 #}
{% endfor %}
</ul>
{# 说明:输出用户总数 #}
<p>共 {{ users|length }} 人</p>
</body>
</html>- 在 Flask 中无需手动创建
Environment,直接用render_template('user_list.html', title=..., users=...)即可。
4. 模板继承 #
- 多个页面共享同一套布局(头部、导航、页脚)时,用模板继承避免重复代码。
- 父模板用
{% block 块名 %}定义可替换区域。 - 子模板用
{% extends "父模板.html" %}继承,再用同名{% block %}填充内容。
templates/base.html(父模板):
{# 说明:HTML 文档声明 #}
<!DOCTYPE html>
{# 说明:html 根元素 #}
<html>
{# 说明:页面头部 #}
<head>
{# 说明:title 块,子模板可覆盖,默认「我的网站」 #}
<title>{% block title %}我的网站{% endblock %}</title>
</head>
{# 说明:页面主体 #}
<body>
{# 说明:网站头部区域 #}
<header><h1>网站头部</h1></header>
{# 说明:主内容区域,子模板填充 content 块 #}
<main>{% block content %}{% endblock %}</main>
{# 说明:页脚版权信息 #}
<footer>© 2026 示例公司</footer>
</body>
</html>templates/profile.html(子模板):
{# 说明:继承 base.html 父模板 #}
{% extends "base.html" %}
{# 说明:覆盖 title 块 #}
{% block title %}个人中心{% endblock %}
{# 说明:填充 content 块 #}
{% block content %}
{# 说明:输出用户名 #}
<h2>欢迎,{{ user.name }}</h2>
{# 说明:个人信息说明文字 #}
<p>这里是个人信息页。</p>
{# 说明:结束 content 块 #}
{% endblock %}- 子模板没有重写的 block,会显示父模板中的默认内容。
- Flask 项目里
base.html+ 各页面extends是最常见的布局方式。
5. 引入子模板(include) #
{% include '片段.html' %}把另一个模板的内容原样插入当前位置。- 适合抽取页头、页脚、侧边栏等公共 HTML 片段。
- 当前页面的变量会自动传递给被 include 的模板。
templates/header.html:
{# 说明:导航栏 #}
<nav>
{# 说明:首页链接 #}
<a href="/">首页</a>
{# 说明:关于页链接 #}
<a href="/about">关于</a>
</nav>templates/page.html:
{# 说明:引入 header.html 导航片段 #}
{% include 'header.html' %}
{# 说明:主内容区 #}
<main>
{# 说明:页面标题 #}
<h1>{{ title }}</h1>
{# 说明:页面正文 #}
<p>{{ content }}</p>
</main>- 继承(extends) 用于整体页面布局;包含(include) 用于插入局部片段。
- 二者经常配合:
base.html做布局,header/footer 用 include 引入。
6. 安全与自动转义 #
- Jinja2 3.x 默认不自动转义;渲染 HTML 时必须手动开启
autoescape(Flask 已自动处理)。 - 自动转义会把
<、>、&等转为 HTML 实体,防止 XSS 攻击。 - 只有明确可信的 HTML 才用
|safe,用户输入绝不能加safe。
{# 说明:自动转义开启时,user_input 中的 HTML 标签会被转义 #}
{{ user_input }}
{# 说明:仅用于已确认安全的 HTML,慎用 #}
{{ trusted_html|safe }}
{# 说明:手动强制 HTML 转义 #}
{{ user_input|e }}- 模板里不能执行任意 Python 代码,这是 Jinja2 的安全设计。
- 传给模板的用户数据,在 Python 侧也应先校验,不要完全依赖模板层防护。
7. 常见问题 #
- 遇到问题按下面顺序排查。
7.1 模板找不到(TemplateNotFound) #
- 确认
FileSystemLoader的路径指向正确的templates/目录。 - 文件名大小写要一致(Linux 服务器上区分大小写)。
- Flask 项目中模板必须放在应用或蓝图指定的
templates/下。
7.2 变量显示为空 #
- 检查
render()传入的变量名是否与模板里一致。 - 用
{{ var|default('无') }}给可能为空的变量设默认值。 - 对象属性不存在时会报错或显示空,Python 侧先保证数据结构完整。
7.3 UndefinedError 或未定义变量 #
- 模板引用了
render()未传入的变量会报错(取决于Environment的undefined配置)。 - 开发阶段可在
render()时传入所有模板需要的变量,避免遗漏。
8. API 速查 #
- 下面汇总日常开发最常用的写法。
| 操作 | 写法 | ||
|---|---|---|---|
| 字符串模板 | Template("{{ x }}").render(x=1) |
||
| 加载文件模板 | Environment(loader=FileSystemLoader("templates")) |
||
| 开启 HTML 转义 | autoescape=select_autoescape(["html", "xml"]) |
||
| 获取模板 | env.get_template("a.html") |
||
| 渲染 | template.render(name="Tom", users=[]) |
||
| 输出变量 | {{ name }} |
||
| 条件 | {% if %}...{% elif %}...{% else %}...{% endif %} |
||
| 循环 | {% for x in xs %}...{% endfor %} |
||
| 定义块 | {% block content %}{% endblock %} |
||
| 继承 | {% extends "base.html" %} |
||
| 引入片段 | {% include "header.html" %} |
||
| 过滤器 | `{{ val\ | upper }}、{{ val\ |
default('') }}` |
| Flask 渲染 | render_template("a.html", name="Tom") |
- 核心流程:写模板 → Python 传入数据 →
render()→ 得到 HTML 字符串。 - Flask 项目优先用
render_template(),不必手动管理Environment。