jinja2 - 强大的模板引擎
1. 项目简介
Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全:
<title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>特性:
- 沙箱中执行
- 强大的 HTML 自动转义系统保护系统免受 XSS
- 模板继承
- 及时编译最优的 Python 代码
- 可选提前编译模板的时间
- 易于调试,异常的行数直接指向模板中的对应行
- 可配置的语法
选择 Jinja 作为名字是因为 Jinja 是日本寺庙的名称,并且 temple 和 template 的发音类似。它并不是以乌干达的金贾市(Jinja)命名的。
快速安装:
pip install -U Jinja2基本示例:
from jinja2 import Template
template = Template('Hello {{ name }}!')
template.render(name='John Doe')
# 'Hello John Doe!'通过创建一个 Template 的实例,你会得到一个新的模板对象,提供一个名为 render() 的方法,该方法在有字典或关键字参数时调用扩充模板。字典或关键字参数会被传递到模板,即模板 “上下文”。
2. jinja2 语法
官方文档
jinja2 语法 官方文档。
2.1 基础语法
Jinja2 既是模板引擎,也是一种模板语言。
Jinja2 语言可以使用任意扩展名,但也可以设置为 .jinja 来确保 IDE 可以增强编写体验。
Jinja2 模版语言类似于 Python,比较符合 Python 语法,但有很多语法不同。
{% 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 %}Jinja2 模版语言,是不区分缩进的,和纯 Python 不同。
注释:
{# 这是注释 #}变量:
{{ post.title }}字典元素:
{{ your_dict['key'] }}列表元素:
{{ your_list[0] }}方法或函数:
{{ obj.somemethod() }}语句:
{% ... %}多行代码块:
{% begin %}
...
{% end %}分隔符:
{% ... %}语句{{ ... }}模板表达式{# ... #}注释
基础示例 1:
{% if user %}
{{ user }}
{% else %}
hello!
{% for index in indexs %}
{{ index }}
{% endfor %}基础示例 2:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>My Webpage</h1>
{{ a_variable }}
{# a comment #}
</body>
</html>赋值语句:
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}2.2 过滤器
过滤器的语法为 var | filter,有点类似于 Unix 的管道,返回将 filter 应用于 var 的结果。
{# 带参数 #}
{{ var | filter(*args) }}
{# 不带参数 #}
{{ var | filter }}过滤器可以一次调用多个:
{{ "hello world" | reverse | upper }}文本块调用(将中间的所有文字都作为变量内容传入到过滤器中):
{% filter upper %}
一大堆文字
{% endfilter %}常用过滤器:
| 字符串操作 | 功能 |
|---|---|
safe | 禁用转义 |
e | 转义字符串 |
capitalize | 首字母大写 |
upper | 转为大写 |
lower | 转为小写 |
title | 每个单词首字母都转为大写 |
reverse | 反转 |
format | 格式化 |
striptags | 去除标签 |
truncate | 截断字符串 |
trim | 去掉首位空字符 |
replace | 替换操作 |
示例:
{{ '<em>hello</em>' | safe }}
{# '<em>hello</em>' #}
{{ 'hello every one' | truncate(9)}}
{# 'hello...' #}
{{ '<em>hello</em>' | striptags }}
{# 'hello' #}
{{ '%s is %d' | format('name',17) }}
{# 'name is 17' #}列表操作:
| 列表操作 | 功能 |
|---|---|
first | 第一个元素 |
last | 最后一个元素 |
length | 列表长度 |
sum | 列表和 |
sort | 排序后的列表 |
示例:
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>其他常见操作:
| 其他操作 | 功能 |
|---|---|
default | 默认值 |
join | 相当于字符串的 .join(...) |
int | 转为整数 |
round | 四舍五入 |
escape | 转义 |
first | 第一个 |
示例:
{{ my_variable | default('my_variable is not defined') }}
{# 如果没有定义返回 'my_variable is not defined' #}
{{ items | join(', ') }}
{# '1, 2, 3, 4' 当 items = [1, 2, 3, 4] 时 #}| 过滤器 |
|---|
abs() |
forceescape() |
map() |
select() |
unique() |
attr() |
format() |
max() |
selectattr() |
upper() |
batch() |
groupby() |
min() |
slice() |
urlencode() |
capitalize() |
indent() |
pprint() |
sort() |
urlize() |
center() |
int() |
random() |
string() |
wordcount() |
default() |
items() |
reject() |
striptags() |
wordwrap() |
dictsort() |
join() |
rejectattr() |
sum() |
xmlattr() |
escape() |
last() |
replace() |
title() |
filesizeformat() |
length() |
reverse() |
tojson() |
first() |
list() |
round() |
trim() |
float() |
lower() |
safe() |
truncate() |
2.3 测试
使用 is equalto 判断是否相等:
{% if user.age is equalto 42 %}
{# 这里也可以写成... is equalto(42) #}
Ha, you are 42!
{% endif %}全部测试:
| 测试 |
|---|
boolean() |
even() |
in() |
mapping() |
sequence() |
callable() |
false() |
integer() |
ne() |
string() |
defined() |
filter() |
iterable() |
none() |
test() |
divisibleby() |
float() |
le() |
number() |
true() |
eq() |
ge() |
lower() |
odd() |
undefined() |
escaped() |
gt() |
lt() |
sameas() |
upper() |
2.4 分支和循环
if 语句:
{% if kenny.sick %}
Kenny is sick.
{% elif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay -- so far
{% endif %}使用 for 语句在使用之前可以判断是否为空,因为模板引擎不会检查 users 是否存在:
{% if users %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}break 和 continue 也可以使用:
{% for user in users %}
{%- if loop.index >= 10 %}{% break %}{% endif %}
{%- endfor %}loop.index:循环当前迭代(从 1 开始)loop.index0:循环当前迭代(从 0 开始)loop.revindex:循环迭代的数量(从 1 开始)loop.revindex0:循环迭代的数量(从 0 开始)loop.first:是否为迭代的第一步loop.last:是否为迭代的最后一步loop.length:序列中元素的数量
一些全局函数:
<ul>
{% for user in users %}
<li>{{ user.username }}</li>
{% endfor %}
{% for number in range(10 - users|count) %}
<li class="empty"><span>...</span></li>
{% endfor %}
</ul>一些全局函数:
| 全局函数 | 功能 |
|---|---|
range() | 相当于 Python range() |
lipsum() | 生成段落测试 |
dict() | 相当于 Python dict() |
cycler() | 生成序列的循环 |
joiner() | 连接字符串对象 |
namespace() | 创建新的命名空间 |
2.5 宏
宏类似于 Python 中的函数,我们在宏中定义行为,还可以进行传递参数。定义一个宏的关键字是 macro,后面跟宏的名称和参数等。
{% macro input(name,age=18) %}
<input type='text' name="{{ name }}" value="{{ age }}" >
{% endmacro %}2.6 继承和骨架
extends 可以让我们引用别的文件来自动插入:
{% extends "base.html" %}配合 block 语法可以构成页面的骨架:
{% block footer %}
footer para.
{% endblock %}其中 footer 可以被替换为 title、head、sidebar、content 等。block 同名的对象只能定义一次,但可以被多次引用:
<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}2.7 扩展语法
执行但不输出:
{% do navigation.append('a string') %}Debug 输出:
<pre>{% debug %}</pre>With 语句运行声明局域变量:
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
foo is not visible here any longer
{% with a={}, b=a.attribute %}...{% endwith %}
{% with foo = 42 %}
{{ foo }}
{% endwith %}
{% with %}
{% set foo = 42 %}
{{ foo }}
{% endwith %}区域内转义:
{% autoescape true %}
Autoescaping is active within this block
{% endautoescape %}
{% autoescape false %}
Autoescaping is inactive within this block
{% endautoescape %}附录:基准测试
我们相当厌烦基准测试,尤其是因为它们并不能影响什么。一个模板的性能取决于许多因素,而你可能需要在不同环境中对不同的引擎做基准测试。测试套件中的基准测试表明,Jinja2 与 Mako 的性能相近,比 Django 的模板引擎或 Genshi 快 10 到 20 倍。这些数字应该相当有刺激性,因为基准测试只测试一些性能相关的场景,比如循环,来获取这些数字。大体上,一个模板引擎的性能几乎不会成为一个 Web 应用的瓶颈,而应该是 数据库或应用的代码。