前言

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
{% 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
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. 参考文章