前言

Flask和Django,以及其它很多Python框架如Ansible,都默认使用Jinja2来作为模版引擎。我们用Jinja2在服务器上直接生成配置和其他文件。 Jinja是一个基于Python设计语言的“全功能模板引擎”,个人认为Jinja语法本身并不复杂,但掌握好基本的Jinja语法会帮助你在构建Ansible、Jenkins、Web等批处理作业时做到事半功倍的效果。

模板引擎Jinja2语法介绍

更新历史

2020年02月20日 - 初稿

阅读原文 - https://wsgzao.github.io/post/jinja/


Jinja简介

Jinja is a modern and designer-friendly templating language for Python, modelled after Django’s templates. It is fast, widely used and secure with the optional sandboxed template execution environment:

1
2
3
4
5
6
7
8
9
{% extends "base.html" %}
{% block title %}Members{% endblock %}
{% block content %}
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
{% endblock %}

Features:

  • sandboxed execution
  • powerful automatic HTML escaping system for XSS prevention
  • template inheritance
  • compiles down to the optimal python code just in time
  • optional ahead-of-time template compilation
  • easy to debug. Line numbers of exceptions directly point to the correct line in the template.
  • configurable syntax

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

特性:

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

为什么要叫Jinja?

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

Jinja的速度怎么样?

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

把逻辑判断(Logic)放到模板里是个好主意吗?

毫无疑问,你放到模板里逻辑判断(Logic)应该越少越好。但为了让大家都开心,适当的逻辑判断是需要的。尽管如此,它有很多对于你能做什么,不能做什么的限制。

出于诸多考虑(速度,易读性等等),Jinja既不允许你放置任意的Python代码,也不允许所有的Python表达式。这也是为什么我们要了解Jinja2的语法。

在Jinja官方文档中建议大家可以优先阅读以下2个章节:

什么是模版引擎

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

1
比如我们在HTML模版中输入一个`<p> {{ post.title }} </p>`,显然这不是真正的HTML语法。但是当Jinja2解释器读取到`{{ ...}}`后知道里面是一个变量,那么就把这个变量替换为真正的值,最后翻译出来就变成了`<p> 大标题 </p>`这样的HTML内容。

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

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

Jinja2语言基础

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

常用标记:

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

示例:

1
2
3
4
5
6
7
8
{% 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
{{ 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
27
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>

List of Builtin Filters

Tests(测试,判断)

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

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

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

常用的test(未说明的均返回True或False):

  • boolean
  • defined
  • equalto
  • escaped
  • none
  • sequence
  • string
  • number
  • reverse
  • replace

List of Builtin Tests

For/If (列表控制结构)

A control structure refers to all those things that control the flow of a program - conditionals (i.e. if/elif/else), for-loops, as well as things like macros and blocks. With the default syntax, control structures appear inside blocks.

List of Control Structures

For

Loop over each item in a sequence. For example, to display a list of users provided in a variable called users:

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

As variables in templates retain their object properties, it is possible to iterate over containers like 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

The if statement in Jinja is comparable with the Python if statement. In the simplest form, you can use it to test if a variable is defined, not empty and not false:

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

For multiple branches, elif and else can be used like in Python. You can use more complex Expressions there, too:

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 %}

Ansible Jinja2 模版使用

更多用法可以阅读参考文章中的链接

variables: 可以输出数据

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变量。

参考文章

Jinja Documentation

Jinja2 中文文档

Flask模板引擎:Jinja2语法介绍

一篇文章搞懂Jinja2 Template Engine 模版引擎

Ansible Templating (Jinja2)

ansible笔记(38):jinja2模板(一)

Ansible Jinja2 模板使用

文章目录
  1. 1. 前言
  2. 2. 更新历史
  3. 3. Jinja简介
  4. 4. 什么是模版引擎
  5. 5. Jinja2语言基础
    1. 5.1. Delimiters(分隔符)
    2. 5.2. Variables(变量)
    3. 5.3. Filter 过滤器()
    4. 5.4. Tests(测试,判断)
  6. 6. For/If (列表控制结构)
    1. 6.1. For
    2. 6.2. If
  7. 7. Ansible Jinja2 模版使用
  8. 8. 参考文章