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() 的方法,该方法在有字典或关键字参数时调用 扩充模板。字典或关键字参数会被传递到模板,即模板“上下文”。
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
导入其他模板
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 3 4 5
| <div> {% if True %} yay {% endif %} </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 4 5
| <div> {%- if True -%} yay {%- endif -%} </div>
|
渲染后会是这样:
通过Jinja2提供的环境变量,你可以设置很多东西,比如分隔符(在和其他的语言产生冲突时,可以通过修改分隔符来解决)。具体见:http://jinja.pocoo.org/docs/dev/api/#jinja2.Environment
参考资料
文章链接:
https://www.zywvvd.com/notes/coding/doc/jinja2/jinja2/