文章目录
Jinja2 简介
Jinja2 是一个功能强大的 Python 模板引擎,它继承了Django模板引擎的精髓;
更在此基础上进行了诸多创新性的扩展,为用户提供了更为丰富和灵活的功能选项;借助Jinja2,可以在模板中轻松运用变量、逻辑控制结构(如条件判断和循环迭代)以及自定义函数,以生成HTML、XML或任何其他类型的文本格式;
这使得Jinja2成为了一个高效且强大的工具,能够帮助开发者在构建Web应用程序时实现更为优雅和动态的页面内容生成。
Jinja2 安装
Jinja2 属于Python的第三方库,需要额外下载安装,命令如下:
pip install jinja2
Jinja2 原理
- 加载和解析模板:
使用Environment类和相应的加载器(如FileSystemLoader)加载模板文件;
Jinja2 解析模板文件,将其转换为抽象语法树(AST);
AST 是模板的内部表示,它描述了模板的结构和内容;- 变量替换:
Jinja2 遍历 AST,查找所有的变量占位符(通常由双大括号**{{ variable }}**标记);
使用render
方法传递的变量值替换这些占位符;
如果某个变量在渲染时未提供,Jinja2 可以配置抛出一个异常或保留原始占位符;- 执行控制结构:
Jinja2 会处理模板中的控制结构,如if、for循环和with语句等;
这些控制结构会影响最终输出文本的生成方式,根据条件或循环迭代的结果动态地包含或排除某些部分;- 应用过滤器和函数:
Jinja2 允许在变量上应用过滤器来转换或格式化数据;
例如,可以使用内置的safe过滤器来标记字符串为安全的HTML;
还可以定义自定义过滤器或函数,并在模板中像内置过滤器一样使用它们;- 模板继承和包含:
Jinja2 支持模板继承,这意味着可以定义一个基础模板(通常包含页面的通用布局和区块),然后其他模板可以继承这个基础模板,并覆盖或添加特定的区块;
模板包含允许将一个模板的内容包含到另一个模板中。这通常用于复用模板片段。- 生成最终输出:
一旦所有的变量替换、控制结构执行、过滤器和函数应用、模板继承和包含处理完毕,Jinja2 会生成最终的文本输出。
这个输出可以是 HTML、XML、文本文件或任何需要的文本格式。
Jinja2 三种语法
- 变量取值 {{ }}
- 控制结构(逻辑代码) {% %}
- 注释 {# #}
Jinja2 核心功能
- 变量:在 Jinja2 模板中,你可以使用双大括号
{{ variable_name }}
来表示变量。
这些变量在渲染模板时会被替换为实际的值。
例如,如果你有一个名为name
的变量,其值为 “John”;
那么在模板中{{ name }}
将被替换为 “John”。- 控制结构:Jinja2 支持常见的逻辑控制结构,
如if
、for
和while
;
这些控制结构在模板中用于根据条件或循环来生成不同的内容;
例如,你可以使用{% if condition %}
来根据条件判断是否渲染某段内容;- 过滤器:过滤器允许你对变量进行转换或格式化;
它们通常在变量后面使用管道符号|
来表示;
例如,可以使用{{ variable_name|length }}
来获取变量variable_name
的长度。
Jinja2 提供了许多内置过滤器,如length
、lower
、upper
等,
你也可以自定义过滤器;- 宏:宏是 Jinja2 中的一个强大功能,它允许你定义可重用的代码块;
你可以将常用的代码块定义为宏,并在需要的地方调用它们;
宏使用{% macro name(arguments) %}
和{% endmacro %}
来定义,并通过{{ macro_name(arguments) }}
来调用。- 继承:Jinja2 支持模板继承,这意味着你可以定义一个基础模板,并在其他模板中继承它。这有助于保持代码的一致性和重用性。
在基础模板中,你可以使用{% block block_name %}
和{% endblock %}
来定义可覆盖的块;
然后在继承模板中使用{% extends "base_template.html" %}
和{% block block_name %}
来覆盖或添加内容。- 包含:除了继承,Jinja2 还支持模板包含。
这意味着你可以在一个模板中使用{% include "other_template.html" %}
来包含另一个模板的内容。这有助于将模板拆分为更小的、更易于管理的部分。- 注释:在 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 还提供了一些高级的循环控制结构;
比如
break
、continue
和loop.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 结构;
如页眉、页脚、导航栏等,同时仍然能够在子模板中覆盖或添加特定的内容。
模板继承的基本工作原理
- 定义基础模板:这个模板包含你想要在所有子模板中重用的 HTML 结构和块(blocks)。
块是通过{% block %}
和{% endblock %}
标签定义的。- 创建子模板:子模板使用
{% extends %}
标签来继承基础模板。
然后,子模板可以覆盖基础模板中的某些块,或者添加新的块。- 渲染子模板:当子模板被渲染时,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
中,我们重写了title
、header
、content
和footer
块。对于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的包含功能可以带来很多好处,比如:
- 代码重用:避免在多个模板中重复编写相同的HTML代码。
- 易于维护:如果需要修改被包含的模板内容,只需要在一个地方进行修改,而不需要去查找和修改所有使用这个模板的地方。
- 组织性:通过将模板分解为更小的、更易于管理的部分,可以提高代码的组织性和可读性。
基础模板 (
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代码片段(例如表单、按钮、导航栏等)封装起来,从而在多个模板中重用这些代码。
宏的优点
- 代码重用:宏允许你将常用的代码片段封装起来,并在多个模板中重复使用。
- 参数化:宏可以接受参数,使得它们更加灵活和可配置。
- 组织性:通过将相关的代码片段放入单独的宏文件中,你可以更好地组织你的模板代码。
注意事项
- 宏定义应该放在单独的模板文件中,这样可以在多个模板中导入和使用它们。
- 宏名应该是唯一的,避免与其他宏或变量名冲突。
- 在调用宏时,确保传递了所有必需的参数。
假设你有一个名为
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_button
和render_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>,不会被转义为 <html> -->
# 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都能提供强大的支持,是开发者不可或缺的模板渲染工具。