Flask 和 Django,以及其它很多 Python 框架如 Ansible,都默认使用 Jinja2 来作为模版引擎。我们用 Jinja2 在服务器上直接生成配置和其他文件。Jinja 是一个基于 Python 设计语言的“全功能模板引擎”,个人认为 Jinja 语法本身并不复杂,但掌握好基本的 Jinja 语法会帮助你在构建 Ansible、Jenkins、Web 等批处理作业时做到事半功倍的效果。
模板引擎 Jinja2 语法介绍
2020 年 02 月 20 日 - 初稿
阅读原文 - https://wsgzao.github.io/post/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:
{% 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:
Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 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 语言
的标记语言的解释器。
比如我们在 HTML 模版中输入一个`<p> {{ post.title }} </p>`,显然这不是真正的 HTML 语法。但是当 Jinja2 解释器读取到`{{ ...}}`后知道里面是一个变量,那么就把这个变量替换为真正的值,最后翻译出来就变成了`<p> 大标题 </p>`这样的 HTML 内容。
Jinja2 是一个模版语言,只是类似 Python,比较符合 Python 语法,但不完全相同!
所有的模版引擎,实际上都差不多,不管是基于 VBS 语言的 ASP 模版,还是基于 PHP 语言的 PHP 模版,都不是与原本语言一摸一样,而只是做到尽量一样而已。
注意:Jinja2
模版语言,是不区分缩进的,和纯 python 不同。实际上所有模版语言都不区分缩紧。
常用标记:
注释:`{# 这是注释 #}`
变量:`{{ post.title }}`,或字典元素`{{your_dict['key']}}`,或列表`{{your_list[0]}}`
多行代码块:`{% 开始 %} HTML 标签 {% 结束 %}`
示例:
{% if user %}
{{ user }}
{% else %}
hello!
{% for index in indexs %}
{{ index }}
{% endfor %}
{% … %} 语句([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))
除了普通的字符串变量,Jinja2 还支持列表、字典和对象,你可以这样获取变量值:
{{ mydict['key'] }}
{{ mylist[3] }}
{{ mylist[myintvar] }}
{{ myobj.somemethod() }}
获取一个变量的属性有两种方式:
{{ foo.bar }}
{{ foo['bar'] }}
这两种方法基本相同(深层次的区别可以暂不考虑)
一个 filter 过滤器的本质就是一个 function 函数。使用格式为:变量名 | 函数
。
它做到的就是,把变量传给函数,然后再把函数返回值作为这个代码块的值。
如:
<!-- 带参数的 -->
{{变量 | 函数名(*args)}}
<!-- 不带参数可以省略括号 -->
{{变量 | 函数名}}
链式调用(管道式): 和命令行的 pipline 管道一样,可以一次调用多个函数(过滤器),如:
{{ "hello world" | reverse | upper }}
文本块调用(将中间的所有文字都作为变量内容传入到过滤器中):
{% filter upper %}
一大堆文字
{% endfilter %}
Jinja2 常用过滤器
字符串操作:
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>
列表操作:
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>
Jinja2 提供的 tests 可以用来在语句里对变量或表达式进行测试,如果要测试一个变量,可以在变量后加上“is”和 test 名,比如:
{% if user.age is equalto 42 %} {# 这里也可以写成... is equalto(42) #}
Ha, you are 42!
{% endif %}
如果要传入参数,可以在 test 后增加括号,也可以直接写在后面。
常用的 test (未说明的均返回 True 或 False ):
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.
Loop over each item in a sequence. For example, to display a list of users provided in a variable called users:
<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:
<dl>
{% for key, value in my_dict.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
</dl>
循环索引
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:
{% 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:
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
更多用法可以阅读参考文章中的链接
variables: 可以输出数据
{{ my_variable }}
{{ some_dudes_name | capitalize }}
statements: 可以用来创建条件和循环等等
{% if my_conditional %}
xxx
{% endif %}
{% for item in all_items %}
{{ item }}
{% endfor %}
从上文中第二个 variable 的例子中可以看出,Jinja2 支持使用带过滤器的 Unix 型管道操作符。有很多的内置过滤器可供使用。
我们可以仅仅用一堆简单 if 和 for 就可以建立建立几乎任何的常规配置文件。不过如果你有意更进一步,Jinja2 Documentation 包含了很多有趣的东西可供了解。我们可以看到 Ansibe 允许在模板中使用一些额外的模版变量。
按照 Ansible template_module, 我们模板的示例:
- name: Create Nginx SSL configuration
template:
src: "nginx_ssl.j2"
dest: "/etc/nginx/sites-available/{{ project_name }}"
我们同样可以发现在 Ansible Facts 中有很多可用的 Ansible 变量。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.