文章目录
Jinja2 是一个功能强大的 Python 模板引擎,由 Flask 作者 Armin Ronacher 开发,广泛用于 Web 开发(如 Flask、FastAPI 等框架)和文档生成场景。它支持变量替换、控制结构(条件判断、循环)、模板继承、宏定义等高级功能,语法简洁直观,兼具灵活性和安全性。
一、Jinja2 核心特点
- 强大的变量处理:支持访问字典、列表、对象属性,甚至调用函数/方法。
- 丰富的控制结构:提供
if-else条件判断、for循环等,逻辑处理灵活。 - 模板复用机制:通过
继承(extends)、包含(include)、宏(macro)实现模板模块化。 - 安全特性:默认自动转义 HTML 特殊字符,防止 XSS 攻击。
- 可扩展性:支持自定义过滤器(Filters)、测试器(Tests)和全局函数。
二、安装与基本概念
安装
pip install jinja2 # 支持 Python 3.6+
核心概念
- 模板(Template):包含固定内容和动态占位符的文本文件(如 HTML、Markdown 等)。
- 上下文(Context):传递给模板的变量/数据集合(字典形式)。
- 环境(Environment):Jinja2 的核心对象,管理模板加载、配置(如自动转义、变量分隔符等)。
- 加载器(Loader):负责从文件系统、字符串或其他来源加载模板(常用
FileSystemLoader从本地文件加载)。
三、基础用法:从模板到渲染
Jinja2 的核心流程是:创建环境 → 加载模板 → 传递上下文 → 渲染输出。
1. 最小示例:字符串模板渲染
from jinja2 import Template
# 1. 定义模板字符串(包含动态变量 {{ name }})
template_str = "Hello, {{ name }}! You are {{ age }} years old."
# 2. 创建模板对象
template = Template(template_str)
# 3. 传递上下文数据(字典形式)并渲染
result = template.render(name="Alice", age=30)
print(result) # 输出:Hello, Alice! You are 30 years old.
2. 从文件加载模板(常用场景)
实际开发中,模板通常存储在文件中(如 templates 目录),需通过 FileSystemLoader 加载。
项目结构
myproject/
├── app.py # 主程序
└── templates/ # 模板目录
└── greeting.html # 模板文件
app.py文件代码
from jinja2 import Environment, FileSystemLoader
import os
# 1. 配置环境:指定模板目录(绝对路径更可靠)
template_dir = os.path.join(os.path.dirname(__file__), "templates")
env = Environment(loader=FileSystemLoader(template_dir)) # 用FileSystemLoader加载文件
# 2. 加载模板文件(templates/greeting.html)
template = env.get_template("greeting.html")
# 3. 定义上下文数据
context = {
"user": {"name": "Bob", "hobbies": ["reading", "coding", "hiking"]},
"is_member": True
}
# 4. 渲染模板
html_output = template.render(**context)
# 打印结果或写入文件
print(html_output)
with open("output.html", "w", encoding="utf-8") as f:
f.write(html_output)
templates/greeting.html文件代码
<!DOCTYPE html>
<html>
<head>
<title>Greeting</title>
</head>
<body>
<h1>Hello, {{ user.name }}!</h1> <!-- 访问对象属性/字典值 -->
{% if is_member %} <!-- 条件判断 -->
<p>Welcome back, member!</p>
{% else %}
<p>Please sign up to become a member.</p>
{% endif %}
<p>Your hobbies:</p>
<ul>
{% for hobby in user.hobbies %} <!-- 循环遍历列表 -->
<li>{{ loop.index }}. {{ hobby }}</li> <!-- loop.index 是循环索引(从1开始) -->
{% endfor %}
</ul>
</body>
</html>
渲染输出(简化)
<!DOCTYPE html>
<html>
<head><title>Greeting</title></head>
<body>
<h1>Hello, Bob!</h1>
<p>Welcome back, member!</p>
<p>Your hobbies:</p>
<ul>
<li>1. reading</li>
<li>2. coding</li>
<li>3. hiking</li>
</ul>
</body>
</html>
四、核心语法与特性
1. 变量与数据访问
模板中通过 {{ 变量名 }} 引用上下文数据,支持多种数据类型:
| 数据类型 | 模板中访问方式 | 示例(上下文:{"user": {"name": "Alice"}, "scores": [90, 85]}) |
|---|---|---|
| 字典/对象属性 | {{ dict.key }} 或 {{ obj.attr }} | {{ user.name }} → “Alice” |
| 列表/元组 | {{ list[index] }} | {{ scores[0] }} → 90 |
| 函数/方法调用 | {{ func() }} (需在上下文定义函数) | 上下文含 {"add": lambda a,b: a+b} → {{ add(2,3) }} → 5 |
2. 控制结构
条件判断(if-elif-else)
{% if score >= 90 %}
<p>优秀</p>
{% elif score >= 60 %}
<p>及格</p>
{% else %}
<p>不及格</p>
{% endif %}
循环(for)
<!-- 遍历列表,使用 loop 变量获取循环信息 -->
<ul>
{% for item in items %}
<li>
{{ loop.index }}. {{ item }} <!-- loop.index:从1开始的索引 -->
{% if loop.first %}(第一个){% endif %}
{% if loop.last %}(最后一个){% endif %}
</li>
{% else %} <!-- 列表为空时执行 -->
<li>无数据</li>
{% endfor %}
</ul>
3. 模板继承(extends 与 block)
模板继承是实现模板复用的核心机制,通过 extends 继承父模板,用 block 重写父模板的可变部分。
父模板(templates/base.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}默认标题{% endblock %}</title> <!-- 可被重写的块 -->
<style>
{% block style %}{% endblock %} <!-- 样式块 -->
</style>
</head>
<body>
<header>网站头部</header>
<main>
{% block content %}{% endblock %} <!-- 内容块(核心可变部分) -->
</main>
<footer>网站底部</footer>
</body>
</html>
子模板(templates/home.html)
{% extends "base.html" %} <!-- 继承父模板 -->
{% block title %}首页{% endblock %} <!-- 重写标题块 -->
{% block style %} <!-- 重写样式块 -->
main { color: #333; }
{% endblock %}
{% block content %} <!-- 重写内容块 -->
<h2>欢迎来到首页</h2>
<p>这是首页的具体内容</p>
{% endblock %}
渲染子模板
# 代码中加载子模板,会自动应用父模板结构
template = env.get_template("home.html")
print(template.render())
4. 包含其他模板(include)
通过 include 可将一个模板嵌入到另一个模板中(适合复用小片段,如导航栏、广告位)。
被包含的模板(templates/nav.html)
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
在其他模板中包含
<!-- 在 base.html 中包含导航 -->
<body>
{% include "nav.html" %} <!-- 嵌入导航模板 -->
<main>{% block content %}{% endblock %}</main>
</body>
5. 宏(macro):模板中的“函数”
宏用于定义可复用的代码片段(类似函数),支持参数传递,适合生成重复 HTML 结构(如表格行、按钮)。
{# 定义宏(可放在单独的 macros.html 中,通过 include 引入) #}
{% macro button(text, type="primary") %}
<button class="btn btn-{{ type }}">
{{ text }}
</button>
{% endmacro %}
{# 使用宏 #}
{{ button("提交") }} <!-- 生成:<button class="btn btn-primary">提交</button> -->
{{ button("取消", type="danger") }} <!-- 生成:<button class="btn btn-danger">取消</button> -->
6. 过滤器(Filters):处理变量
过滤器用于对变量进行格式化或转换,语法:{{ 变量 | 过滤器名(参数) }}。Jinja2 内置多种过滤器,也支持自定义。
常用内置过滤器
| 过滤器 | 功能示例({{ value | 过滤器 }}) |
|----------------|-----------------------------------------------------------|
| upper | 转为大写:"hello" → "HELLO" |
| lower | 转为小写:"HELLO" → "hello" |
| capitalize | 首字母大写:"hello" → "Hello" |
| truncate(n) | 截断字符串到 n 长度:"hello world" | truncate(5) → "hello..." |
| join(sep) | 拼接列表:[1,2,3] | join(",") → "1,2,3" |
| default(val) | 变量为 None 时用默认值:undefined_var | default("默认") |
| safe | 标记为安全内容(关闭自动转义):"<h1>hi</h1>" | safe |
示例
<p>大写:{{ "hello" | upper }}</p> <!-- 输出:大写:HELLO -->
<p>列表拼接:{{ [1,2,3] | join("-") }}</p> <!-- 输出:列表拼接:1-2-3 -->
<p>默认值:{{ user.nickname | default("匿名用户") }}</p>
自定义过滤器
from jinja2 import Environment, FileSystemLoader
# 1. 定义自定义过滤器:将字符串反转
def reverse_filter(s):
return s[::-1]
# 2. 在环境中注册过滤器
env = Environment(loader=FileSystemLoader("templates"))
env.filters["reverse"] = reverse_filter # 注册名为 reverse 的过滤器
# 3. 模板中使用(templates/filter_demo.html)
# {{ "hello" | reverse }} → "olleh"
template = env.get_template("filter_demo.html")
print(template.render())
7. 安全与自动转义
Jinja2 默认会自动转义 HTML 特殊字符(如 < 转为 <),防止 XSS 攻击。若需关闭转义(如变量包含可信 HTML),可使用 safe 过滤器:
{# 自动转义(安全) #}
{{ "<script>alert('xss')</script>" }}
<!-- 输出:<script>alert('xss')</script> -->
{# 关闭转义(仅用于可信内容) #}
{{ "<h1>安全HTML</h1>" | safe }}
<!-- 输出:<h1>安全HTML</h1> -->
五、与 Web 框架集成(以 Flask 为例)
Flask 内置 Jinja2 作为模板引擎,无需额外配置即可使用,默认模板目录为 templates。
app.py文件代码
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/>')
def user_profile(name):
# 传递上下文数据到模板
user_data = {
"name": name,
"age": 28,
"skills": ["Python", "Flask", "Jinja2"]
}
# 渲染 templates/profile.html 模板
return render_template("demo.html", user=user_data)
if __name__ == '__main__':
app.run(debug=True)
templates/demo.html文件代码
<h1>{{ user.name }}的个人资料</h1>
<p>年龄:{{ user.age }}</p>
<p>技能:</p>
<ul>
{% for skill in user.skills %}
<li>{{ skill }}</li>
{% endfor %}
</ul>
六、高级技巧
1. 模板缓存
生产环境中,可启用模板缓存(默认关闭)提升性能:
env = Environment(
loader=FileSystemLoader("templates"),
auto_reload=False, # 关闭自动重载(生产环境)
cache_size=400 # 缓存模板数量
)
2. 全局变量与函数
通过 env.globals 注册全局变量或函数,在所有模板中可用:
# 注册全局函数
def current_time():
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d")
env = Environment(loader=FileSystemLoader("templates"))
env.globals["current_time"] = current_time # 模板中可直接用 {{ current_time() }}
3. 测试器(Tests)
测试器用于判断变量是否满足特定条件(语法:{{ 变量 is 测试器 }}),如 defined(是否定义)、none(是否为 None):
{% if user is defined %}
<p>用户已定义</p>
{% endif %}
{% if message is none %}
<p>无消息</p>
{% endif %}
七、总结
Jinja2 凭借其简洁的语法、强大的复用机制和安全特性,成为 Python 生态中最流行的模板引擎之一。核心优势包括:
- 模板继承:通过
extends和block实现页面结构复用,减少重复代码。 - 灵活的数据处理:支持多种数据类型访问和过滤器转换。
- 安全性:默认自动转义,防止 XSS 攻击。
- 框架兼容性:无缝集成 Flask、FastAPI 等主流 Web 框架。
无论是 Web 页面渲染、邮件模板生成还是静态站点构建,Jinja2 都是高效且可靠的选择。
2065

被折叠的 条评论
为什么被折叠?



