1. 核心特性 #
Jinja2 是一个用 Python 编写的现代、设计友好的模板引擎。它被广泛用于生成动态内容,特别是在 Web 开发中(如 Flask、Django 的模板系统),但也常用于配置文件生成、邮件模板等场景。
1.1 模板语法 #
Jinja2 的模板语法非常灵活,主要分为三大类:
- 注释
{# ... #}:用于模板中写说明性文字,不会被渲染到输出结果中。 - 变量
{{ ... }}:用于插入变量,输出变量的值。变量内容可以是复杂表达式,也能调用对象的属性和方法。 - 语句/控制结构
{% ... %}:用于流程控制,比如条件判断、循环、继承、宏等。
Jinja2 支持类似 Python 的表达式和语法,能方便地书写模板逻辑。同时,它通过“过滤器”提供对变量的进一步加工处理,如格式化字符串、大小写转换、拼接列表等。
例子说明:
{{ user.name|upper }}会把user.name的内容转成大写后输出。{% if items %}...{% endif %}实现条件渲染。{{ list|join(', ') }}把列表用逗号拼接成字符串。
Jinja2 模板语法利于前后端分离,书写自然、可读性强,对开发网页、配置文件等极为方便。
{# 注释 #}
{{ 变量 }} {# 变量输出 #}
{% 语句 %} {# 控制语句 #}1.2 变量与过滤器 #
Jinja2 变量是模板中动态数据的载体。模板里通过 {{ ... }} 语法插入变量,变量名由渲染时传入的上下文(dict)决定。支持访问属性、下标、链式取值等。例如:
{{ user }}直接输出变量 user{{ user.name }}访问对象属性{{ users[0] }}访问列表元素{{ config["key"] }}访问字典数据
过滤器(filter)用于对变量做进一步处理,语法为 {{ 变量|过滤器名(参数) }}。常见过滤器:
upper/lower:大小写转换。如{{ name|upper }}default:若变量不存在/值为假,使用默认值。如{{ name|default('匿名') }}length:获取长度。{{ list|length }}join:拼接序列(列表、元组等)为字符串。如{{ tags|join(', ') }}replace:字符串替换。{{ text|replace('a', 'b') }}safe:标记内容为 HTML 安全,不会被自动转义
还可以链式使用多个过滤器,如:{{ username|trim|lower|default('guest') }}
Jinja2 内置几十种过滤器,也可自定义过滤器。
1.3 控制结构 #
Jinja2 的控制结构让模板具备类似 Python 的流程控制能力。主要有三类:
条件判断(if/elif/else)
用于根据变量或表达式的值选择性地渲染内容。通常语法和 Python 类似。{% if score >= 90 %} 优 {% elif score >= 60 %} 及格 {% else %} 不及格 {% endif %}循环 for
用于遍历序列(列表、字典、集合等)、生成枚举列表。loop变量可以辅助获取下标、是否首/尾元素等。<ul> {% for user in users %} <li>{{ loop.index }}. {{ user.name }} ({{ user.email }})</li> {% endfor %} </ul>loop.index:当前迭代从 1 开始的编号loop.first/loop.last:是否首个/最后一个loop.length:循环总长度
其它结构
Jinja2 还支持set(设置变量)、macro(自定义宏/函数)、include(引入子模板)、with快捷变量传递等。例如:{% set welcome = "你好, " + user.name %} {{ welcome }}
特别说明:
- 控制结构必须用 {% ... %} 包裹,不能用 {{ ... }}。
- if/for 可嵌套使用,符合 Python 语法习惯。
- 仅在 for 内容无数据时渲染 else 子句(不是 for 循环结束后)。
{% for item in items %} ... {% else %} 暂无内容 {% endfor %}
这些控制结构大幅提高了模板的灵活性,适用于业务数据展示、条件内容输出、动态生成列表表格等各种场景。
1.4 模板继承 #
Jinja2 的模板继承功能,可以让多个页面共享相同的模板结构(如统一的页眉、页脚、导航栏),仅在部分位置替换各自不同的内容。通过继承机制,提升了模板复用性与可维护性。
基础模板(父模板)
使用{% block 块名 %}...{% endblock %}声明可被子模板替换或填充的区域。通常基础模板用于定义通用布局结构,例如网站的整体页面框架。子模板
用{% extends "父模板.html" %}指定继承的父模板,然后用{% block %}替换(重写)父模板中对应的内容块。
示例说明:
base.html(基础模板)
<!DOCTYPE html> <html> <head> <title>{% block title %}我的网站{% endblock %}</title> </head> <body> <header> <h1>网站头部</h1> </header> <nav> <!-- 导航菜单 --> </nav> <main> {% block content %}{% endblock %} </main> <footer> © 2024 示例公司 </footer> </body> </html>child.html(子模板,继承 base.html)
{% extends "base.html" %} {% block title %}个人中心{% endblock %} {% block content %} <h2>欢迎回来,{{ user.name }}</h2> <p>这里是您的个人信息页面。</p> {% endblock %}
- 这样,子模板自动获得父模板的整体结构(头部、导航、页脚),只需关心填充
title和content的内容,极大提升模板的复用性和开发效率。 - 可以有多个
{% block %},允许不同页面按需仅覆盖局部区域,不被重写的块会显示父模板原内容。
2. 基本使用示例 #
Jinja2 模板的基本用法非常简单,常见的步骤如下:
模板定义
- 可以用字符串直接创建模板,也可以从文件加载 HTML/Jinja2 文件作为模板。
数据渲染
- 使用
render()方法,把准备好的变量/字典传给模板,生成最终的 HTML 字符串。
- 使用
文件加载典型目录结构
- 实践中模板通常放在一个
templates/文件夹,通过FileSystemLoader加载。
- 实践中模板通常放在一个
示例流程(推荐实际尝试):
直接渲染字符串模板
# 导入Jinja2中用于模板渲染的Template类 from jinja2 import Template # 创建一个模板对象,模板内容中用{{ name }}占位 template = Template("你好,{{ name }}!") # 使用render方法将变量name的值传入模板,生成最终字符串 html = template.render(name="小明") # 输出渲染后的字符串结果 print(html) # 输出:你好,小明!渲染文件模板
假设有templates/hello.html:<h1>Hi, {{ username }}!</h1>加载与渲染:
# 导入 Jinja2 的 Environment 和 FileSystemLoader,用于环境配置和模板文件加载 from jinja2 import Environment, FileSystemLoader # 创建一个模板环境,指定模板文件夹位置为 "templates" env = Environment(loader=FileSystemLoader("templates")) # 加载名为 "hello.html" 的模板文件 template = env.get_template("hello.html") # 使用 render 方法传入变量 username,渲染模板生成最终字符串 html = template.render(username="Jinja用户") # 输出渲染后的 HTML 字符串 print(html)传递对象、列表等复杂数据
- Jinja2 会自动支持字典、列表、Python 对象等,模板中可以用点(.)或方括号([])访问属性。
- 支持循环、判断、过滤器(|)等功能。
总结:
- Jinja2 模板渲染即“模板 + 数据 = HTML字符串”
- 模板语法自然,变量用
{{ }},代码逻辑用{% %}
2.1 Python 代码 #
# 导入Jinja2的Template、Environment与FileSystemLoader
from jinja2 import Template, Environment, FileSystemLoader
# 方式1:直接用字符串创建模板对象
template = Template('Hello {{ name }}!')
# 用 render 方法渲染模板,传入变量name
result = template.render(name='World')
# 输出渲染结果
print(result) # Hello World!
# 方式2:从文件夹加载模板
env = Environment(loader=FileSystemLoader('templates'))
# 获取名为'my_template.html'的模板文件
template = env.get_template('my_template.html')
# 渲染模板,传递title和users变量
result = template.render(title='首页', users=['Alice', 'Bob'])2.2 模板文件 my_template.html #
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>用户列表</h1>
<ul>
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">
{{ user }}
{% if user == 'Alice' %} (管理员){% endif %}
</li>
{% endfor %}
</ul>
<p>用户数量: {{ users|length }}</p>
</body>
</html>3. 高级功能 #
3.1 自定义过滤器 #
自定义过滤器可以让你在模板表达式中对变量进行特殊处理。例如常见的自定义时间格式化:
- 定义过滤器函数:接收输入值和参数,返回处理结果。
- 注册到Jinja2环境:将函数与过滤器名称绑定。
示例:
# 定义一个将datetime对象格式化为指定字符串的过滤器
def datetime_format(value, format='%Y-%m-%d %H:%M'):
return value.strftime(format)
# 方式2:从文件夹加载模板
env = Environment(loader=FileSystemLoader('templates'))
# 注册到模板环境,命名为datetime_format
env.filters['datetime_format'] = datetime_format
# 获取名为'my_template.html'的模板文件
template = env.get_template('my_template.html')
# 渲染模板,传递title和users变量
result = template.render(title='首页', users=['Alice', 'Bob'],now=datetime.now())
print(result)模板调用方法:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>用户列表</h1>
<ul>
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">
{{ user }}
{% if user == 'Alice' %} (管理员){% endif %}
</li>
{% endfor %}
</ul>
<p>用户数量: {{ users|length }}</p>
<p>当前时间: {{ now|datetime_format('%Y-%m-%d') }}</p>
</body>
</html>常见用途:
- 格式化日期/时间
- 字符串大小写转换
- 金额格式化
- 过滤敏感内容等
自定义过滤器让模板输出更灵活丰富。
3.2 宏(类似于函数) #
宏(macro)类似于编程语言中的函数,可以将模板中经常复用的片段(如表单控件、按钮等)进行抽象,简化页面结构,方便维护和重用。
主要特点:
- 用
{% macro 宏名(参数) %}...{% endmacro %}定义。 - 可带参数,可有默认值。
- 在模板中可多次调用,减少重复代码。
- 支持条件判断、循环等模板语句。
示例说明:
下面示例中,定义了一个 input 宏,可用于渲染不同类型的表单输入控件。通过传递不同参数,宏可以灵活生成各种输入框:
# 导入Jinja2模块中的Template、Environment和FileSystemLoader类
from jinja2 import Template, Environment, FileSystemLoader
# 导入datetime模块中的datetime类
from datetime import datetime
# 通过字符串创建一个Jinja2模板对象
template = Template(
"""
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{{ input('username') }}
{{ input('password', type='password') }}
"""
)
# 渲染模板,无需传递变量
result = template.render()
# 打印渲染结果
print(result) # Hello World!宏可在页面任意位置调用,并根据传参输出不同内容,非常适合构建复杂的表单、表格、按钮组等 UI 片段。
常见应用:
- 表单控件生成
- 按钮组件
- 表格行/列渲染
- 可复用的布局片段
3.3 包含模板 #
包含模板(include)用于将其他模板的内容插入当前模板,方便页面结构复用、模块化开发。
主要用法:
- 用
{% include '文件名' %}语句引入外部模板片段 - 通常用于页头、页脚、导航栏等公共部分的抽取
- 被包含的模板可以是完整页面,也可以是 HTML 片段
特点:
- 当前模板的变量会传递给被包含的模板,可以直接使用
- 支持在任何地方包含,可嵌套使用
简单案例:
例如,有三个模板文件:
header.html:头部导航footer.html:底部版权main.html:主题内容
在主页面模板中:
{% include 'header.html' %}
主体内容
{% include 'footer.html' %}最终渲染结果会自动拼接 header、主体、footer,为页面开发带来极大便利。
3.4 空白控制 #
空白控制(Whitespace Control)用于精细调整 Jinja2 模板渲染时生成的 HTML 代码中的空行和缩进,减少不必要的空白,输出更为简洁。
主要用法:
- 在 Jinja2 语法标签(如
{% ... %}或{{ ... }})的开始或结束处添加减号(-),去除标签前或后的空白符。{%- ... %}:去除标签前的空白{% ... -%}:去除标签后的空白- 两侧一起用:
{%- ... -%},去除前后空白
- 适用于 for、if、block、include 等所有 Jinja2 块
案例说明: 原始模板如下,未做空白控制:
<ul>
{% for user in users %}
<li>{{ user }}</li>
{% endfor %}
</ul>渲染结果中每个<li>之间都会有换行和缩进。
如做空白控制:
<ul>
{%- for user in users %}
<li>{{ user }}</li>
{%- endfor %}
</ul>渲染结果会去除多余的空行,使列表紧凑。
更多示例:
控制 if 块渲染的空白:
{% if active -%} <p>激活状态!</p> {%- endif %}同时去除变量标签两端的空白:
{{- name -}}这样
name变量前后的所有空格都会被去除。
小结:
- 推荐对循环、条件语句、include 等适度加减号,保持输出页面整洁
- 复杂模板建议配合编辑器显示空白字符,方便调试
{% for item in items -%} {# 减号去除空白 #}
{{ item }}
{%- endfor %}4. 安全特性 #
Jinja2 模板引擎内置多种安全机制,有助于防止常见的 Web 安全问题,主要包括:
1. 自动转义(Autoescape) #
- 默认自动转义 HTML 特殊字符(如
<,>,&等),有效防止 XSS(跨站脚本攻击)。 - 仅当渲染为 HTML/XML 输出时自动启用,对纯文本或自定义类型需手动开启。
2. |safe 过滤器 #
- 某些情况下输出已经明确安全的 HTML,可用
|safe过滤器防止其被再次转义。 - 使用时需 确保内容可信,否则容易引入安全隐患。
3. 手动转义与强制转义 #
|escape或简写|e过滤器,可强制对变量进行 HTML 转义。- 支持嵌套:可以多次转义内容。
4. 禁用代码执行 #
- Jinja2 仅限模板语法,不允许模板中直接执行任意 Python 代码(如
os.system),隔离了危险操作。
5. 配置和过滤器加固 #
- 建议只将可信数据传入模板。
- 可自定义过滤器对特定内容进行二次过滤,增强安全性。
注意事项:
- 自动转义只对变量起效。模板中的静态 HTML、JS 需开发者自行防护。
- 动态生成的 JS、CSS、属性等内容,建议配合转义函数或模板过滤器。
举例说明:
{{ user_input }} {# 自动转义,防XSS #}
{{ user_input|safe }} {# 明确信任,取消转义,注意风险 #}
{{ user_input|escape }}{# 手动强制转义 #}通过合理利用 Jinja2 的安全特性,可以大大降低模板渲染带来的安全风险,保障 Web 应用安全。
5. 常见应用场景 #
- Web 开发:Flask、Django 模板
- 配置管理:Ansible、SaltStack 的模板文件
- 文档生成:API 文档、报告生成
- 邮件模板:动态邮件内容
- 代码生成:根据模板生成代码文件
6. 最佳实践 #
- 逻辑与表现分离:模板中尽量少做复杂计算
- 使用模板继承:保持布局一致性
- 合理使用宏:复用 UI 组件
- 配置自动转义:确保安全
- 缓存模板:生产环境中提升性能