Python模板引擎Jinja2库的功能介绍、使用和代码示例

文章目录

Jinja2 是一个功能强大的 Python 模板引擎,由 Flask 作者 Armin Ronacher 开发,广泛用于 Web 开发(如 Flask、FastAPI 等框架)和文档生成场景。它支持变量替换、控制结构(条件判断、循环)、模板继承、宏定义等高级功能,语法简洁直观,兼具灵活性和安全性。

一、Jinja2 核心特点

  1. 强大的变量处理:支持访问字典、列表、对象属性,甚至调用函数/方法。
  2. 丰富的控制结构:提供 if-else 条件判断、for 循环等,逻辑处理灵活。
  3. 模板复用机制:通过 继承(extends)包含(include)宏(macro) 实现模板模块化。
  4. 安全特性:默认自动转义 HTML 特殊字符,防止 XSS 攻击。
  5. 可扩展性:支持自定义过滤器(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. 模板继承(extendsblock

模板继承是实现模板复用的核心机制,通过 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 特殊字符(如 < 转为 &lt;),防止 XSS 攻击。若需关闭转义(如变量包含可信 HTML),可使用 safe 过滤器:

{# 自动转义(安全) #}
{{ "<script>alert('xss')</script>" }}  
<!-- 输出:&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt; -->

{# 关闭转义(仅用于可信内容) #}
{{ "<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 生态中最流行的模板引擎之一。核心优势包括:

  • 模板继承:通过 extendsblock 实现页面结构复用,减少重复代码。
  • 灵活的数据处理:支持多种数据类型访问和过滤器转换。
  • 安全性:默认自动转义,防止 XSS 攻击。
  • 框架兼容性:无缝集成 Flask、FastAPI 等主流 Web 框架。

无论是 Web 页面渲染、邮件模板生成还是静态站点构建,Jinja2 都是高效且可靠的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值