本文最后更新于:2025年8月13日 晚上

Jinja2 是一个 Python 的功能齐全的模板引擎。它有完整的 unicode 支持,一个可选 的集成沙箱执行环境,被广泛使用,以 BSD 许可证授权,本文记录相关内容。

简介

Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全。

Jinja2 需要至少 Python 2.4 版本来运行。此外,如果你使用 Python 2.4 ,一个可 以创建 python 扩展的可用的 C 编译器会为调试器安装。

特性:

  • 沙箱中执行
  • 强大的 HTML 自动转义系统保护系统免受 XSS
  • 模板继承
  • 及时编译最优的 python 代码
  • 可选提前编译模板的时间
  • 易于调试。异常的行数直接指向模板中的对应行。
  • 可配置的语法

为什么要叫Jinja?

之所以叫Jinja,是因为日本的神社(Jinja)英文单词是temple,而模板的英文是template,两者发音很相似(这么说来,它本来也有可能叫Miao的……)。

Jinja的速度怎么样?

和Mako差不多,但比Genshi以及Django的模板引擎快10~20倍。

安装

1
pip install Jinja2 docxtpl

基本 API 使用

最基本的方式就是通过 Template 创建一个模板并渲染它。

1
2
3
from jinja2 import Template
template = Template('Hello {{ name }}!')
template.render(name='John Doe')

通过创建一个 Template 的实例,你会得到一个新的模板对象,提供一 个名为 render() 的方法,该方法在有字典或关键字参数时调用 扩充模板。字典或关键字参数会被传递到模板,即模板“上下文”。

  • 如果需要结合 Word 的模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from docxtpl import DocxTemplate, InlineImage
from docx.shared import Pt

# 加载模板
doc = DocxTemplate("template.docx")

# 准备数据
context = {
'title': '项目报告',
'user': {
'name': '张三',
'email': 'zhangsan@example.com'
},
'tasks': [
{'description': '需求分析', 'deadline': '2023-12-31'},
{'description': '代码开发', 'deadline': '2024-01-15'}
]
}

# 渲染Word文档
doc.render(context)
output_docx = "output.docx"
doc.save(output_docx)

模版引擎

在Python中,什么是模版?就是在一个静态HTML加入一些类似变量的标签,然后引擎在渲染这个HTML时候会动态的把变量填入内容,生成一个最终的HTML。
什么是模版引擎?其实就是一种能解析类似Python语言的标记语言的解释器。

Jinja2是一个模版语言,只是类似Python,比较符合Python语法,但不完全相同!

所有的模版引擎,实际上都差不多,不管是基于VBS语言的ASP模版,还是基于PHP语言的PHP模版,都不是与原本语言一摸一样,而只是做到尽量一样而已。

语言基础

注意:Jinja2模版语言,是不区分缩进的,和纯python不同。实际上所有模版语言都不区分缩紧。

常用标记:

1
2
3
注释:`{# 这是注释 #}`
变量:`{{ post.title }}`,或字典元素`{{your_dict['key']}}`,或列表`{{your_list[0]}}`
多行代码块:`{% 开始 %} HTML标签 {% 结束 %}`

示例:

1
2
3
4
5
6
7
{% if user %}
{{ user }}
{% else %}
hello!
{% for index in indexs %}
{{ index }}
{% endfor %}

Delimiters(分隔符)

1
2
3
4
{% … %} 语句([Statements](http://jinja.pocoo.org/docs/dev/templates/#list-of-control-structures))
{{ … }} 打印模板输出的表达式([Expressions](http://jinja.pocoo.org/docs/dev/templates/#expressions))
{# … #} 注释
# … ## 行语句([Line Statements](http://jinja.pocoo.org/docs/dev/templates/#line-statements))

Variables

除了普通的字符串变量,Jinja2还支持列表、字典和对象,你可以这样获取变量值:

1
2
3
4
{{ mydict['key'] }}
{{ mylist[3] }}
{{ mylist[myintvar] }}
{{ myobj.somemethod() }}
1
2
{{ my_variable }}
{{ some_dudes_name | capitalize }}

statements: 可以用来创建条件和循环等等

1
2
3
4
5
6
7
{% if my_conditional %}
xxx
{% endif %}

{% for item in all_items %}
{{ item }}
{% endfor %}

从上文中第二个variable的例子中可以看出,Jinja2支持使用带过滤器的Unix型管道操作符。有很多的内置过滤器可供使用。

我们可以仅仅用一堆简单if和for就可以建立建立几乎任何的常规配置文件。不过如果你有意更进一步,Jinja2 Documentation包含了很多有趣的东西可供了解。我们可以看到Ansibe允许在模板中使用一些额外的模版变量。

按照Ansible template_module, 我们模板的示例:

1
2
3
4
- name: Create Nginx SSL configuration
template:
src: "nginx_ssl.j2"
dest: "/etc/nginx/sites-available/{{ project_name }}"

我们同样可以发现在Ansible Facts中有很多可用的Ansible变量。

获取一个变量的属性有两种方式:

1
2
{{ foo.bar }}
{{ foo['bar'] }}

Filter

一个filter过滤器的本质就是一个function函数。使用格式为:变量名 | 函数
它做到的就是,把变量传给函数,然后再把函数返回值作为这个代码块的值。

如:

1
2
3
4
5
<!-- 带参数的 -->
{{变量 | 函数名(*args)}}

<!-- 不带参数可以省略括号 -->
{{变量 | 函数名}}

链式调用(管道式):
和命令行的pipline管道一样,可以一次调用多个函数(过滤器),如:

1
{{ "hello world" | reverse | upper }}

文本块调用(将中间的所有文字都作为变量内容传入到过滤器中):

1
2
3
{% filter upper %}
一大堆文字
{% endfilter %}

Jinja2常用过滤器

字符串操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>

capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>

lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>

upper:把值转成大写
<p>{{ 'hello' | upper }}</p>

title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>

reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>

format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>

striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>

truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>

列表操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>

last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>

length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>

sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>

sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>

Tests

Jinja2提供的tests可以用来在语句里对变量或表达式进行测试,如果要测试一个变量,可以在变量后加上“is”和test名,比如:

1
2
3
{% if user.age is equalto 42 %} {# 这里也可以写成... is equalto(42) #}
Ha, you are 42!
{% endif %}

如果要传入参数,可以在test后增加括号,也可以直接写在后面。

For

循环浏览序列中的每个项目。例如,要显示名为 users 的变量中提供的用户列表:

1
2
3
4
5
6
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>

由于模板中的变量保留了它们的对象属性,因此可以迭代像 dict 这样的容器:

1
2
3
4
5
6
<dl>
{% for key, value in my_dict.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>

循环索引

  • loop.index: 循环当前迭代(从1开始)。
  • loop.index0: 循环当前迭代(从0开始)。
  • loop.revindex: 循环迭代的数量(从1开始)。
  • loop.revindex0: 循环迭代的数量(从0开始)。
  • loop.first: 是否为迭代的第一步。
  • loop.last: 是否为迭代的最后一步。
  • loop.length: 序列中元素的数量。

If

Jinja 中的 if 语句与 Python if 语句相当。在最简单的形式中,您可以使用它来测试变量是否已定义,而不是空的,也不是假的:

1
2
3
4
5
6
7
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}

对于多个分支,可以像在 Python 中一样使用 elif 和 else。您也可以在那里使用更复杂的表达式 :

1
2
3
4
5
6
7
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}

include

导入其他模板

1
{%include "sub.tpl"%}

extends

继承母版

1
{%extends 'base.conf.tpl'%}

你可以创建一个base.html作为基模板,把导航栏、页脚、flash消息、js或css文件等等需要在每一个页面中显示的内容放在基模板里,并添加一个空的块用来放置其他子模板的内容:

1
{% block content %}{% endblock %}

然后在其他的模板(子模板)里使用这个extends语句继承它,并放置相应的内容到基模板里定义过的空块:

1
2
3
4
{% extends "base.html" %}
{% block content %}
子模板的内容
{% endblock %}

如果想添加内容到在父模板内已经定义的块,可以使用super函数:

1
2
3
4
5
{% block sidebar %}
<h3>Table Of Contents</h3>
...
{{ super() }}
{% endblock %}

这样可以避免覆盖父块的内容。

Whitespace Control(空格控制)

默认的设置:

  1. 如果末尾有换行符,则去除;
  2. 其他空格原样保留。

也就是说,下面这几行:

1
2
3
4
5
<div>
{% if True %}
yay
{% endif %}
</div>

渲染后的结果是这样:

1
2
3
4
5
<div>

yay

</div>

Jinja2语句占据的空行,你自己输出的空格,Tab都将保留。

如果要去掉Jinja2语句占据的空行,可以通过设置Jinja2的环境变量实现:

1
2
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True

或者像这样手动添加一个减号(注意和%之间没有空格):

1
2
3
4
5
<div>
{% if True -%}
yay
{%- endif %}
</div>

两者实现的效果相同,如下:

1
2
3
<div>
yay
</div>

如果语句块的前后都加上减号:

1
2
3
4
5
<div>
{%- if True -%}
yay
{%- endif -%}
</div>

渲染后会是这样:

1
<div>yay</div>

通过Jinja2提供的环境变量,你可以设置很多东西,比如分隔符(在和其他的语言产生冲突时,可以通过修改分隔符来解决)。具体见:http://jinja.pocoo.org/docs/dev/api/#jinja2.Environment

参考资料



文章链接:
https://www.zywvvd.com/notes/coding/doc/jinja2/jinja2/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

Jinja2
https://www.zywvvd.com/notes/coding/doc/jinja2/jinja2/
作者
Yiwei Zhang
发布于
2025年8月13日
许可协议