Python 功能强大且灵活的模板引擎Jinja2模块



Jinja2 简介

Jinja2 是一个功能强大的 Python 模板引擎,它继承了Django模板引擎的精髓
更在此基础上进行了诸多创新性的扩展,为用户提供了更为丰富和灵活的功能选项;

借助Jinja2,可以在模板中轻松运用变量、逻辑控制结构(如条件判断和循环迭代)以及自定义函数,以生成HTML、XML或任何其他类型的文本格式;

这使得Jinja2成为了一个高效且强大的工具,能够帮助开发者在构建Web应用程序时实现更为优雅和动态的页面内容生成。

Jinja2 安装

Jinja2 属于Python的第三方库,需要额外下载安装,命令如下:

pip install jinja2

Jinja2 原理

  1. 加载和解析模板
    使用Environment类和相应的加载器(如FileSystemLoader)加载模板文件;
    Jinja2 解析模板文件,将其转换为抽象语法树(AST);
    AST 是模板的内部表示,它描述了模板的结构和内容;
  2. 变量替换
    Jinja2 遍历 AST,查找所有的变量占位符(通常由双大括号**{{ variable }}**标记);
    使用 render 方法传递的变量值替换这些占位符;
    如果某个变量在渲染时未提供,Jinja2 可以配置抛出一个异常或保留原始占位符;
  3. 执行控制结构
    Jinja2 会处理模板中的控制结构,如if、for循环和with语句等
    这些控制结构会影响最终输出文本的生成方式,根据条件或循环迭代的结果动态地包含或排除某些部分;
  4. 应用过滤器和函数
    Jinja2 允许在变量上应用过滤器来转换或格式化数据
    例如,可以使用内置的safe过滤器来标记字符串为安全的HTML;
    还可以定义自定义过滤器或函数,并在模板中像内置过滤器一样使用它们;
  5. 模板继承和包含
    Jinja2 支持模板继承,这意味着可以定义一个基础模板(通常包含页面的通用布局和区块),然后其他模板可以继承这个基础模板,并覆盖或添加特定的区块
    模板包含允许将一个模板的内容包含到另一个模板中。这通常用于复用模板片段。
  6. 生成最终输出
    一旦所有的变量替换、控制结构执行、过滤器和函数应用、模板继承和包含处理完毕,Jinja2 会生成最终的文本输出。
    这个输出可以是 HTML、XML、文本文件或任何需要的文本格式。

Jinja2 三种语法

  • 变量取值 {{ }}
  • 控制结构(逻辑代码) {% %}
  • 注释 {# #}

Jinja2 核心功能

  1. 变量:在 Jinja2 模板中,你可以使用双大括号 {{ variable_name }} 来表示变量。
    这些变量在渲染模板时会被替换为实际的值。
    例如,如果你有一个名为 name 的变量,其值为 “John”;
    那么在模板中 {{ name }} 将被替换为 “John”。
  2. 控制结构:Jinja2 支持常见的逻辑控制结构,
    ifforwhile
    这些控制结构在模板中用于根据条件或循环来生成不同的内容;
    例如,你可以使用 {% if condition %} 来根据条件判断是否渲染某段内容;
  3. 过滤器:过滤器允许你对变量进行转换或格式化;
    它们通常在变量后面使用管道符号 | 来表示;
    例如,可以使用 {{ variable_name|length }} 来获取变量variable_name 的长度。
    Jinja2 提供了许多内置过滤器,如 lengthlowerupper 等,
    你也可以自定义过滤器;
  4. :宏是 Jinja2 中的一个强大功能,它允许你定义可重用的代码块;
    你可以将常用的代码块定义为宏,并在需要的地方调用它们;
    宏使用 {% macro name(arguments) %}{% endmacro %} 来定义,并通过 {{ macro_name(arguments) }} 来调用。
  5. 继承:Jinja2 支持模板继承,这意味着你可以定义一个基础模板,并在其他模板中继承它。这有助于保持代码的一致性和重用性。
    在基础模板中,你可以使用 {% block block_name %}{% endblock %} 来定义可覆盖的块;
    然后在继承模板中使用 {% extends "base_template.html" %}{% block block_name %} 来覆盖或添加内容。
  6. 包含:除了继承,Jinja2 还支持模板包含。
    这意味着你可以在一个模板中使用 {% include "other_template.html" %} 来包含另一个模板的内容。这有助于将模板拆分为更小的、更易于管理的部分。
  7. 注释:在 Jinja2 模板中,你可以使用 {# this is a comment #} 来添加注释。
    注释不会出现在最终渲染的结果中。

基本变量替换

创建一个模板文件:template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>{{ title }}</title>
</head>
<body>
    <p>姓名:{{ name }}</p>
    <p>年龄:{{ age }}</p>
    <p>性别:{{ gender }}</p>
    <p>电话:{{ phone }}</p>
    <p>城市:{{ city }}</p>
    <p>身份证号码:{{ idcard }}</p>
</body>
</html>

使用以下 Python 代码来渲染这个模板

import datetime
from faker import Faker
from jinja2 import Template

# 加载模板文件
with open('template.html') as f:
    template_str = f.read()

# 创建模板对象
template = Template(template_str)

# 利用Faker模块生成虚拟数据
fake = Faker('zh_CN')
name = fake.name()
idcard = fake.ssn(min_age=18, max_age=60)
# 利用随机的身份证号码的倒数第二位数字区分性别
gender = "男" if int(idcard[16:-1]) % 2 != 0 else "女"
# 利用随机的身份证号码的年月日计算年龄
age = int(datetime.datetime.now().year) - int(idcard[6:-8])
phone = fake.phone_number()
city = fake.city_name()

# 渲染模板,传入变量值
rendered_template = template.render(
    title='基本变量替换',
    name=name,
    age=age,
    gender=gender,
    phone=phone,
    idcard=idcard,
    city=city,
)

# 输出渲染后的结果
print(rendered_template)
# <!DOCTYPE html>
# <html lang="en">
# <head>
#     <title>基本变量替换</title>
# </head>
# <body>
#     <p>姓名:邱玲</p>
#     <p>年龄:21</p>
#     <p>性别:女</p>
#     <p>电话:13870866875</p>
#     <p>城市:哈尔滨</p>
#     <p>身份证号码:130101200307220540</p>
# </body>
# </html>


控制结构


if 条件判断

在Jinja2模板引擎中,if条件判断是一种常用的控制流语句,用于根据特定条件渲染不同的模板内容。

你可以使用{% if %}{% elif %}{% else %}标签来构建条件语句,并在满足某个条件时渲染特定的代码块。


创建一个模板文件:example_02_template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>{{ title }}</title>
</head>
<body>
    <p>姓名:{{ name }}</p>
    <p>年龄:{{ age }}</p>
    <p>性别:{{ gender }}</p>
    {% if age > 30 and gender == "女" %}
    大姐姐一枚~
    {% elif age < 30 and gender == "女" %}
    小仙女一只~
    {% elif age > 30 and gender == "男" %}
    大哥哥一位~
    {% elif age < 30 and gender == "男" %}
    小帅哥一只~
    {% endif %}
    <p>电话:{{ phone }}</p>
    <p>城市:{{ city }}</p>
    <p>身份证号码:{{ idcard }}</p>
</body>
</html>

使用以下 Python 代码来渲染这个模板

import datetime
from faker import Faker
from jinja2 import Template

# 加载模板文件
with open('template_02.html') as f:
    template_str = f.read()

# 创建模板对象
template = Template(template_str)

# 利用Faker模块生成虚拟数据
fake = Faker('zh_CN')
name = fake.name()
idcard = fake.ssn(min_age=18, max_age=60)
# 利用随机的身份证号码的倒数第二位数字区分性别
gender = "男" if int(idcard[16:-1]) % 2 != 0 else "女"
# 利用随机的身份证号码的年月日计算年龄
age = int(datetime.datetime.now().year) - int(idcard[6:-8])
phone = fake.phone_number()
city = fake.city_name()

# 渲染模板,传入变量值
rendered_template = template.render(
    title='if 条件判断',
    name=name,
    age=age,
    gender=gender,
    phone=phone,
    idcard=idcard,
    city=city,
)

# 输出渲染后的结果
print(rendered_template)
# <!DOCTYPE html>
# <html lang="en">
# <head>
#     <title>if 条件判断</title>
# </head>
# <body>
#     <p>姓名:苏平</p>
#     <p>年龄:21</p>
#     <p>性别:女</p>
#     
#     小仙女一只~
#     
#     <p>电话:15207638655</p>
#     <p>城市:广州</p>
#     <p>身份证号码:410581200304292347</p>
# </body>
# </html>

for 循环

在Jinja2模板引擎中,for循环是一种常用的控制流语句,用于在模板中迭代一个序列(如列表、元组或字典)并渲染每个元素。


创建一个模板文件:example_03_template.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>{{ title }}</title>
</head>
<body>
{% for data in datas %}
    <p>学生姓名:{{ data[0] }}</p>
    <p>学生性别:{{ data[1] }}</p>
    <p>学生电话:{{ data[2] }}</p>
    <p>学生城市:{{ data[3] }}</p>
    <p>学生身份证号码:{{ data.4 }}</p>
    <br/>
{% endfor %}
</body>
</html>

使用以下 Python 代码来渲染这个模板

import random

from faker import Faker
from jinja2 import Template

# 加载模板文件
with open('template_03.html') as f:
    template_str = f.read()

# 创建模板对象
template = Template(template_str)

# 利用Faker模块生成虚拟数据
datas = []
for i in range(5):
    fake = Faker('zh_CN')
    name = fake.name()
    gender = "男" if random.choice([True, False]) else "女"
    phone = fake.phone_number()
    idcard = fake.ssn(min_age=18, max_age=24)
    city = fake.city_name()
    datas.append([name, gender, phone, idcard, city])

# 渲染模板,传入变量值
rendered_template = template.render(
    title='使用控制结构',
    datas=datas,
)

# 输出渲染后的结果
print(rendered_template)
# <!DOCTYPE html>
# <html lang="en">
# <head>
#     <title>使用控制结构</title>
# </head>
# <body>
# 
#     <p>学生姓名:宋桂珍</p>
#     <p>学生性别:女</p>
#     <p>学生电话:14769834410</p>
#     <p>学生城市:530623200504117059</p>
#     <p>学生身份证号码:香港</p>
#     <br/>
# 
#     <p>学生姓名:曾红</p>
#     <p>学生性别:女</p>
#     <p>学生电话:15389279282</p>
#     <p>学生城市:450100200205178351</p>
#     <p>学生身份证号码:沈阳</p>
#     <br/>
# 
#     <p>学生姓名:卢利</p>
#     <p>学生性别:男</p>
#     <p>学生电话:15561176655</p>
#     <p>学生城市:451400200109285023</p>
#     <p>学生身份证号码:淮安</p>
#     <br/>
# 
#     <p>学生姓名:郑秀英</p>
#     <p>学生性别:女</p>
#     <p>学生电话:15809529510</p>
#     <p>学生城市:532327200409307234</p>
#     <p>学生身份证号码:郑州</p>
#     <br/>
# 
#     <p>学生姓名:褚杨</p>
#     <p>学生性别:男</p>
#     <p>学生电话:15095846391</p>
#     <p>学生城市:610323200106208215</p>
#     <p>学生身份证号码:哈尔滨</p>
#     <br/>
# 
# </body>
# </html>

迭代字典语法

<dl>
{% for key, value in my_dict.iteritems() %}
<dt>{{ key }}</dt>
<dd>{{ value}}</dd>
{% endfor %}
</dl>

除了基本的循环结构之外,Jinja2 还提供了一些高级的循环控制结构;

比如 breakcontinueloop.index0 等。

这些控制结构可以帮助我们更好地控制循环过程。

变量描述
loop.index当前迭代的索引(从1开始)
loop.index0当前迭代的索引(从0开始)
loop.first是否是第一次迭代,返回bool
loop.last是否是最后一次迭代,返回bool
loop.length序列中的项目数量
loop.revindex到循环结束的次数(从1开始)
loop.revindex0到循环结束的次数(从0开始)
# loop.index会给出当前迭代的索引(从1开始)
<ul>
    {% for item in items %}
        <li>{{ loop.index }}: {{ item }}</li>
    {% endfor %}
</ul>

模版继承

Jinja2 的模板继承是一种强大的功能,它允许你定义一个基础模板(通常称为“父模板”或“布局模板”),然后在其他模板中继承这个基础模板

这样,就可以在多个模板之间共享通用的 HTML 结构;
如页眉、页脚、导航栏等,同时仍然能够在子模板中覆盖或添加特定的内容。


模板继承的基本工作原理

  1. 定义基础模板:这个模板包含你想要在所有子模板中重用的 HTML 结构和块(blocks)。
    块是通过 {% block %}{% endblock %} 标签定义的。
  2. 创建子模板:子模板使用 {% extends %} 标签来继承基础模板。
    然后,子模板可以覆盖基础模板中的某些块,或者添加新的块。
  3. 渲染子模板:当子模板被渲染时,Jinja2 会首先加载基础模板,然后用子模板中定义的内容替换基础模板中的相应块。

首先,我们创建一个名为 base.html 的基础模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}父模板标题{% endblock %}</title>
</head>
<body>
    <header>
        {% block header %}
            <h1>父模板头部</h1>
        {% endblock %}
    </header>

    <main>
        {% block content %}父模板主题{% endblock %}
    </main>

    <footer>
        {% block footer %}
            <p>父模板页脚</p>
        {% endblock %}
    </footer>
</body>
</html>

然后,我们创建一个名为 child.html 的子模板,它继承了 base.html

{% extends "base.html" %}

{% block title %}
    子模板标题
{% endblock %}

{% block header %}
    <h1>子模板头部</h1>
{% endblock %}

{% block content %}
    <p>子模板正文.</p>
{% endblock %}

home.html 中,我们重写了 titleheadercontentfooter 块。对于 footer 块;
我们使用 {{ super() }} 来保留并显示基础模板中的 footer 块内容,然后添加了一些额外的页脚内容。


最后,我们使用 Python 代码来渲染 child.html 模板

from jinja2 import Environment, FileSystemLoader

# 创建Jinja2环境并指定模板文件夹
# 配置模板加载器
env = Environment(loader=FileSystemLoader('./templates'))

# 加载并渲染模板
template = env.get_template('home.html')
rendered_template = template.render()

# 输出渲染后的 HTML
print(rendered_template)
# <!DOCTYPE html>
# <html lang="en">
# <head>
#     <meta charset="UTF-8">
#     <title>
#     子模板标题
# </title>
# </head>
# <body>
#     <header>
#     <h1>子模板头部</h1>
#     </header>
#
#     <main>
#     <p>子模板正文.</p>
#     </main>
#
#     <footer>
#             <p>父模板页脚</p>
#     </footer>
# </body>
# </html>

模版包含

在Jinja2模板引擎中,模板的包含(Inclusion)是一种机制,允许你在一个模板中插入另一个模板的内容。

这种操作类似于在编程中的函数调用,你可以将一段常用的HTML代码(比如一个导航栏、一个侧边栏或页脚)放在一个单独的模板文件中,然后在需要的地方通过包含指令将这些内容插入到主模板中。

使用Jinja2的包含功能可以带来很多好处,比如:

  1. 代码重用:避免在多个模板中重复编写相同的HTML代码。
  2. 易于维护:如果需要修改被包含的模板内容,只需要在一个地方进行修改,而不需要去查找和修改所有使用这个模板的地方。
  3. 组织性:通过将模板分解为更小的、更易于管理的部分,可以提高代码的组织性和可读性。

基础模板 (base.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}包含{% endblock %}</title>
</head>
<body>
    <header>
        <h1>包含/复用另一个HTML片段代码</h1>
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <!-- 包含另一个模板 -->
    {% include 'footer.html' %}
</body>
</html>

被包含的模板 (footer.html)

<footer>
    <p>被其他html包含(调用/复用)的代码</p>
    <nav>
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
    </nav>
</footer>

使用以下 Python 代码来渲染这个模板

from jinja2 import Environment, FileSystemLoader

# 创建Jinja2环境并指定模板文件夹
# 配置模板加载器
env = Environment(loader=FileSystemLoader('templates/'))

# 加载并渲染模板
template = env.get_template('base.html')
rendered_template = template.render()

# 输出渲染后的 HTML
print(rendered_template)
# <!DOCTYPE html>
# <html lang="en">
# <head>
#     <meta charset="UTF-8">
#     <title>包含</title>
# </head>
# <body>
#     <header>
#         <h1>包含/复用另一个HTML片段代码</h1>
#     </header>
# 
#     <main>
#     </main>
# 
#     <!-- 包含另一个模板 -->
#     <footer>
#     <p>被其他html包含(调用/复用)的代码</p>
#     <nav>
#         <ul>
#             <li><a href="#">Home</a></li>
#             <li><a href="#">About</a></li>
#             <li><a href="#">Contact</a></li>
#         </ul>
#     </nav>
# </footer>
# </body>
# </html>

在Jinja2模板引擎中,宏(Macros)是一种高级功能;
它允许你定义可重用的代码块,并在模板的多个位置调用这些代码块;
宏类似于函数,可以给它们传递参数,并在需要时重复调用它们;
使用宏,可以将常用的HTML代码片段(例如表单、按钮、导航栏等)封装起来,从而在多个模板中重用这些代码。


宏的优点

  1. 代码重用:宏允许你将常用的代码片段封装起来,并在多个模板中重复使用。
  2. 参数化:宏可以接受参数,使得它们更加灵活和可配置。
  3. 组织性:通过将相关的代码片段放入单独的宏文件中,你可以更好地组织你的模板代码。

注意事项

  • 宏定义应该放在单独的模板文件中,这样可以在多个模板中导入和使用它们。
  • 宏名应该是唯一的,避免与其他宏或变量名冲突。
  • 在调用宏时,确保传递了所有必需的参数。

假设你有一个名为macros.html的模板文件,它定义了一些宏

<!-- macros.html -->
<div class="macros">
    {% macro render_button(label) %}
        <button type="button">{{ label }}</button>
    {% endmacro %}

    {% macro render_input(name, value='') %}
        <input type="text" name="{{ name }}" value="{{ value }}">
    {% endmacro %}
</div>

然后,在index.html模板中,你可以导入这些宏并使用它们

<!-- index.html -->
{% import 'macros.html' as macros %}

<html>
<body>
    <h1>Form Example</h1>
    <form action="/submit" method="post">
        <!-- 使用宏渲染一个按钮 -->
        {{ macros.render_button('Submit') }}

        <!-- 使用宏渲染一个输入框,并传递name和value参数 -->
        {{ macros.render_input('username', 'John Doe') }}
    </form>
</body>
</html>

在这个例子中,macros.html文件定义了render_buttonrender_input两个宏;
index.html模板导入了这些宏,并在表单中使用了它们来渲染按钮和输入框。


过滤器

在Jinja2模板引擎中,过滤器(Filters)是一种强大的功能,用于对变量进行转换和处理,以输出所需格式的文本或数据。

过滤器类似于Python内置函数或自定义函数,它们接受一个值作为输入,并返回经过某种处理后的结果

你可以在模板中通过管道符(|)将变量传递给过滤器。


内置过滤器
过滤器名称说明
safe渲染时值不转义
capitialize把值的首字母转换成大写,其他子母转换为小写
lower把值转换成小写形式
upper把值转换成大写形式
title把值中每个单词的首字母都转换成大写
trim把值的首尾空格去掉
striptags渲染之前把值中所有的HTML标签都删掉
join拼接多个值为字符串
replace替换字符串的值
round默认对数字进行四舍五入,也可以用参数进行控制
int把值转换成整型

内置过滤器函数示例

# lower 将字符串转换为小写
{{ 'HELLO WORLD'|lower }} <!-- 输出: hello world -->

# upper 将字符串转换为大写
{{ 'hello world'|upper }} <!-- 输出: HELLO WORLD -->

# length 返回字符串或列表的长度
{{ 'hello'|length }} <!-- 输出: 5 -->
{{ [1, 2, 3]|length }} <!-- 输出: 3 -->

# safe 标记一个字符串为安全的,即不会对其进行自动转义
{{ '<html>'|safe }} <!-- 输出: <html>,不会被转义为 &lt;html&gt; -->

# truncate 截取字符串到指定的长度,并添加省略号(...)作为后缀
{{ 'Hello, World!'|truncate(9) }} <!-- 输出: Hello, W... -->

自定义过滤器

可以通过Jinja2环境来注册自定义过滤器。

自定义过滤器可以是一个Python函数

该函数接受一个参数(即要过滤的值),并返回处理后的结果。

from jinja2 import Environment

# 定义一个反转字符串的自定义过滤器
def reverse_string(s):
    return s[::-1]

# 创建Jinja2环境
env = Environment()

# 注册自定义过滤器
env.filters['reverse'] = reverse_string

# 加载模板
template = env.from_string('{{ "hello"|reverse }}')

# 渲染模板
print(template.render()) # 输出: olleh

在这个例子中,我们定义了一个名为reverse_string的函数,它接受一个字符串参数s并返回其反转后的结果。

然后,我们创建了一个Jinja2环境,并使用env.filters字典将我们的函数注册为一个名为reverse的过滤器。

最后,在模板中,我们使用{{ "hello"|reverse }}来调用这个自定义过滤器,并输出反转后的字符串。

过滤器链

过滤器可以串联使用,称为过滤器链。

你可以在一个变量上连续应用多个过滤器,如下所示:

strip过滤器首先移除了字符串两端的空格,然后lower过滤器将剩余的字符串转换为小写。

{{ '   Hello, World!   '|strip|lower }} <!-- 输出: hello, world! -->

总结

Jinja2是一个功能全面的模板引擎,它提供了丰富的控制结构和变量处理能力,使得动态内容生成变得简单而高效。

它支持条件语句、循环、宏定义和包含等高级功能,让模板更加灵活和可重用。

此外,Jinja2还提供了自动转义、自定义过滤器、国际化支持等特性,增强了模板的安全性和可定制性。

无论是构建简单的静态页面还是复杂的Web应用,Jinja2都能提供强大的支持,是开发者不可或缺的模板渲染工具。

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

需要休息的KK.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值