Jinja2语法

介绍

大概介绍一下,主要介绍模板(模板仅仅是文本文件。它可以生成任何基于文本的格式(HTML、XML、CSV、LaTex 等等)。它并没有特定的扩展名,.html.xml都是可以的。)模板包含变量或表达式 ,这两者在模板求值的时候会被替换为值。模板中 还有标签,控制模板的逻辑。模板语法的大量灵感来自于 Django和Python。

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

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

—-选自 http://docs.jinkan.org

注意

  • Jinja2需要至少Python2.4版本来运行
  • 使用的时候推荐# -*- coding: utf-8 -*-
    Jinja2内部使用Unicode ,这意味着你需要向渲染函数传递 Unicode 对象或只包含 ASCII 字符的字符串。此外,换行符按照默认 UNIX 风格规定行序列结束( \n )
  • 从Jinja2 2.4版本开始支持自动转义(autoescape参数)。自动转义扩展允许你在模板内开关自动转义特性。如果环境的 autoescape 设定为 False ,它可以被激活。如果是 True 可以被关闭。这个设定的覆盖是有作用域的。

—-选自 W3Cschool Jinja2文档

模板(变量 及 过滤器)

变量

变量可以有两种形式,但实际不太一样,另外注意{{ }}不是变量的一部分,而是打印语句的一部分

  • {{ foo.bar }}

    使用foo.bar实际在python层面做了以下事情

    • 检查foo上是否有一个名为bar的属性
    • 若没有,检查foo中是否有一个bar的项
    • 若没有,返回一个未定义的对象
  • {{ foo['bar'] }}

    -使用foo[‘bar’]如下
    -检查foo中是否有一个名为bar的项
    -若没有,检查foo中是否有一个名为bar的属性
    -若没有,返回一个未定义的对象

变量赋值

1
2
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}

过滤器

变量可以通过过滤器修改。过滤器与变量用管道符|分割,并且也可以用圆括号传递可选参数。多个过滤器可以链式调用
常见的内置过滤器如下:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
abs(number) #取绝对值
attr(obj, name) #获得对象属性
batch(value, linecount, fill_with=None) #批处理项目的过滤器,类似切片
capitalize(s) #将第一个字母大写其余小写
center(value, width=80) #将值的宽度增加到指定值
default(value, default_value=u'', boolean=False) #如果值未定义就设置为默认值,如果要对布尔值使用boolean参数需要为True
dictsort(value, case_sensitive=False, by='key') #对字典进行排序
escape(s) #将字符串中的字符&、'、和'转换为安全的序列。如果您需要在HTML中显示可能包含此类字符的文本,请使用此选项。将返回值标记为标记字符串。
filesizeformat(value, binary=False) #将文件大小格式转化为便于阅读的格式,如MB,G
first(seq) #返回序列中的第一个,字符串列表都可以,字符串如果包括,会以,分割,列表直接拿第一项
float(value, default=0.0) #将输入转换成浮点数,转换失败就返回默认值
forceescape(value) #强制HTML转义。这可能会使转义变量翻倍
format(value, args, kwargs*) #给对象应用python字符串格式,如value="%s - %s"
groupby(value, attribute) #使用公共属性将对象序列分组
indent(s, width=4, indentfirst=False) #返回传递字符串的副本,每行缩进4个空格。第一行没有缩进。如果您想更改空格数或缩进第一行,可以向筛选器传递其他参数
int(value, default=0) #将输入转换成整数,转换失败就返回默认值
join(value, d=u'', attribute=None) #将列表拼接,也可以连接对象的某些属性
last(seq) #返回序列中的最后一个
length(object) #返回序列或映射的项数,最简单可以看str的长度,已经列表的元素数
list(value) #将值转化为列表
lower(s) #将值转化为小写
map() #映射属性,对对象序列应用过滤器或查找属性,如{{ titles|map('lower')|join(', ') }}
pprint(value, verbose=False) #方便调试,美观的打印
random(seq) #从序列中返回随机项目
reject() #通过将测试应用于对象或属性,并在测试成功后拒绝这些对象来过滤对象序列。
rejectattr() #通过将测试应用于对象或属性,并在测试成功后拒绝这些对象来过滤对象序列。
replace(s, old, new, count=None) #查找替换
reverse(value) #反转对象
round(value, precision=0, method='common') #将数字舍入到给定的精度。第一个参数指定精度(默认值为0),第二个参数指定舍入方法
safe(value) #将该值标记为安全,这意味着在启用自动转义的环境中,该变量不会被转义。
select() #通过将测试应用于对象或属性,并仅选择测试成功的对象,过滤对象序列。
selectattr() #通过将测试应用于对象或属性,并仅选择测试成功的对象,过滤对象序列。
slice(value, slices, fill_with=None) #分割迭代器并返回包含这些项目的列表。当创建包含三个表示列的ul标记的div时很有用
sort(value, reverse=False, case_sensitive=False, attribute=None) #排序
string(object) #创建字符串unicode
striptags(value) #去掉SGML/XML标签,用一个空格替换相邻的空格。
sum(iterable, attribute=None, start=0) #返回数字的和,如{{ items|sum(attribute='price') }}
title(s) #返回该值的标题大小写版本。即单词将以大写字母开头,所有剩余字符都是小写的
trim(value) #去掉开头和结尾的空格
truncate(s, length=255, killwords=False, end='...') #截断字符串指定长度
upper(s) #将值转为大写
urlencode(value) #用于网址的转义字符串(使用UTF-8编码)。既接受字典和常规字符串也接受成对的iterables。
urlize(value, trim_url_limit=None, nofollow=False) #将纯文本网址转换为可点击
wordcount(s) #返回字符串中的单词数
wordwrap(s, width=79, break_long_words=True, wrapstring=None) #返回传递给筛选器的字符串的副本,该字符串在指定个字符后换行
xmlattr(d, autospace=True) #基于字典中的项目创建一个SGML/XML属性字符串。所有既不是无也不是未定义的值都会自动转义

过滤器小栗子

1
2
3
{% filter upper %}
This text becomes uppercase
{% endfilter %}

基础语法

测试

测试可以对普通表达式进行测试变量,测试也可以接受参数。如果测试只接受一个参数,你可以省去括号来分组它们

1
{% if loop.index is divisibleby(3) %}

内置测试清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
callable(object) #对象是否可调用
defined(value) #是否定义
divisibleby(value, num) #能否被整除
escaped(value) #是否转义
even(value) #如果是偶数为true
iterable(value) #是否有可迭代对象
lower(value) #小写返回true
mapping(value) #字典返回true
none(value) #空返回true
number(value) #数字返回true
odd(value) #变量为奇数返回true
sameas(value, other) #是否指向相同的内存地址
sequence(value) #列表返回true
string(value) #字符串返回true
undefined(value) #是否未定义
upper(value) #大写返回true

注释&空白控制&转义

注释: 在模板内进行注释使用{# 要注释的内容 #},如

1
2
3
4
5
{# note: disabled template example
{% for user in users %}
...
{% endfor %}
#}

空白控制 :默认情况下,模板引擎不会对空白进行修改,如果配置了Jinja的trim_blocks则模板标签后的第一个换行符会被移除,另外,可以通过在块的开始或结束放置一个减号-
注意:标签和减号之间不能有空格,可以移除块前或块后的空白.

1
2
3
4
5
6
#demo1: 生成不换行的所有元素
{% for item in seq -%}
{{ item }}
{%- endfor %}
#demo2
{%- if foo -%}...{% endif %}

转义:较短的转义可以使用变量表达式,较长的内容或段落可以通过raw

1
2
3
4
5
6
7
8
#demo1
{{ '{' }}
#demo2
{% raw %}
{% for i in [1,2,3] %}
<li>&#123;&#123; i &#125;&#125;</li>
&#123;% endfor %&#125;
&#123;% endraw %&#125;

扩展:
当从模板生成 HTML 时,始终有这样的风险:变量包含影响已生成 HTML 的字符。有两种 解决方法:手动转义每个字符或默认自动转义所有的东西。

  • 使用手动转义: 转义通过用管道传递到过滤器 |e 来实现: {{ user.username|e }} 。
  • 使用自动转义: 当启用了自动转移,默认会转移一切,除非值被显式地标记为安全的

行语句

如果配置启用行语句,就可以把一个行标记为一个句子。例如如果行语句前缀配置为’#’,那么#for item in [1,2,3]&#123;% for item in [1,2,3] %&#125;就是等效的。

  • 行语句可以出现在一行的任意位置,只要它的前面没有文本,提升可读性
  • 如果有未闭合的圆括号、花括号或方括号,那么行语句可以跨越多行
  • 从Jinja2.2版本开始,行注释可以使用了,例如如果配置##为行注释前缀,行中所有##后面的内容都会被忽略(不包括换行符)

模板继承&包含

模板继承可以说是Jinja最强大的部分了,模板继承允许定义一个基础模板,然后定义子模板可以覆盖的块。如下例子

基础模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
&#123;% block head %&#125;
<link rel="stylesheet" href="style.css" />
<title>&#123;% block title %&#125;&#123;% endblock %&#125; - My Webpage</title>
&#123;% endblock %&#125;
</head>
<body>
<div id="content">&#123;% block content %&#125;&#123;% endblock %&#125;</div>
<div id="footer">
&#123;% block footer %&#125;
&copy; Copyright 2008 by <a href="http://gourds.site/">Gourds</a>.
&#123;% endblock %&#125;
</div>
</body>

子模板s1
&#123;% extends%&#125;标签确定继承的模板,也可以访问多级目录下的模板&#123;% extends "path_to/my_template.html" %&#125;。使用super()可以调用super来渲染父级块的内容,会返回父级块的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
&#123;% extends "base.html" %&#125;
&#123;% block title %&#125;Index&#123;% endblock %&#125;
&#123;% block head %&#125;
&#123;&#123; super() &#125;&#125;
<style type="text/css">
.important &#123; color: #336699; &#125;
</style>
&#123;% endblock %&#125;
&#123;% block content %&#125;
<h1>Index Test </h1>
<p class="important">
Gourds test page s1.
</p>
&#123;% endblock %&#125;

推荐使用命名块结束标签提升可读性(endblock后面写的要与block后面的对应),如

1
2
3
4
5
&#123;% block my_data %&#125;
&#123;% block you_data %&#125;
...
&#123;% endblock you_data %&#125;
&#123;% endblock my_data%&#125;

嵌套块和作用域
默认的块不允许访问块外作用域中的变量,嵌套块可以.从Jinja2.2开始,只需在块声明中添加scoped装饰,就可以显示的指定在块中可用的变量,当覆盖一个块时不需要scoped

1
2
3
&#123;% for item in seq %&#125;
<li>&#123;% block loop_item scoped %&#125;&#123;&#123; item &#125;&#125;&#123;% endblock %&#125;</li>
&#123;% endfor %&#125;

包含
Include语句用于包含一个模板,并在当前命名空间返回文件的渲染结果,包含的模板默认可以访问活动的上下文中的变量

1
2
3
&#123;% include 'header.html' %&#125;
Body
&#123;% include 'footer.html' %&#125;

控制结构

for语句

遍历列表中的每一元素,支持大部分python语法。注意不能在循环中使用breakcontinue.但可以通过过滤来跳过项目,如&#123;% for user in users if not user.hidden %&#125;

1
2
3
4
5
6
7
8
&#123;% for k,v in local_data.items() %&#125;
<tr>
<td>&#123;&#123; v['Name'] &#125;&#125;</td>
<td>&#123;&#123; v.Age &#125;&#125;</td>
<td>&#123;&#123; v.Site &#125;&#125;</td>
</tr>
&#123;% endfor %&#125;

如果因序列是空或者过滤移除了序列中的所有项目而没有执行循环,你可以使用 else 渲染一个用于替换的块

1
2
3
4
5
6
7
<ul>
&#123;% for user in users %&#125;
<li>&#123;&#123; user.username|e &#125;&#125;</li>
&#123;% else %&#125;
<li><em>no users found</em></li>
&#123;% endfor %&#125;
</ul>

递归的使用循环

1
2
3
4
5
6
7
8
<ul class="sitemap">
&#123;%- for item in sitemap recursive %&#125;
<li><a href="&#123;&#123; item.href|e &#125;&#125;">&#123;&#123; item.title &#125;&#125;</a>
&#123;%- if item.children -%&#125;
<ul class="submenu">&#123;&#123; loop(item.children) &#125;&#125;</ul>
&#123;%- endif %&#125;</li>
&#123;%- endfor %&#125;
</ul>

在for循环中可用的特殊变量

变量名 描述
loop.index 当前迭代次数从1开始
loop.index0 当前迭代次数从0开始
loop.revindex 到循环结束需要迭代的次数从1开始
loop.revindex0 到循环结束需要迭代的次数从0开始
loop.first 如果是第一次迭代为True
loop.last 如果是最后一次迭代为True
loop.length 列表中的项目数
loop.cycle 在表表间期取值的辅助函数

if语句

跟python中的if差不多,可以使用if、elif、else,可以测试变量或其他表达式,在jinja中也可以叫做内联表达式或者循环过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&#123;% if users %&#125;
<ul>
&#123;% for user in users %&#125;
<li>&#123;&#123; user.username|e &#125;&#125;</li>
&#123;% endfor %&#125;
</ul>
&#123;% endif %&#125;
&#123;% if kenny.sick %&#125;
Kenny is sick.
&#123;% elif kenny.dead %&#125;
You killed Kenny! You bastard!!!
&#123;% else %&#125;
Kenny looks okay --- so far
&#123;% endif %&#125;

关于宏

类似其他语言,宏用来将重复行为分装成可重用的函数.如果宏在不同模板定义需要先import,如果一个宏的名称以下划线开始,则它不能导出且不能被导入
宏内部,可以访问的特殊的变量

  • varargs:多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在 varargs变量上
  • kwargs:同varargs,但只针对关键字参数。所有未使用的关键字参数会存储在这个特殊变量中
  • caller:如果宏通过call标签调用,调用者会作为可调用的宏被存储在这个变量中
  • name:宏的名称
  • arguments:一个宏接受的参数名的元组
  • defaults:默认值的元组
  • catch_kwargs:如果宏接受额外的关键字参数,为true
  • catch_varargs:如果宏接受额外的位置参数,为true
  • caller:如果宏访问特殊的caller变量且由call标签调用,为true

如下例子,定义宏

1
2
3
4
&#123;% macro input(name, value='', type='text', size=20) -%&#125;
<input type="&#123;&#123; type &#125;&#125;" name="&#123;&#123; name &#125;&#125;" value="&#123;&#123;
value|e &#125;&#125;" size="&#123;&#123; size &#125;&#125;">
&#123;%- endmacro %&#125;

使用宏

1
2
<p>&#123;&#123; input('username') &#125;&#125;</p>
<p>&#123;&#123; input('password', type='password') &#125;&#125;</p>

带参数调用块的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
&#123;% macro dump_users(users) -%&#125;
<ul>
&#123;%- for user in users %&#125;
<li><p>&#123;&#123; user.username|e &#125;&#125;</p>&#123;&#123; caller(user) &#125;&#125;</li>
&#123;%- endfor %&#125;
</ul>
&#123;%- endmacro %&#125;
&#123;% call(user) dump_users(list_of_user) %&#125;
<dl>
<dl>Realname</dl>
<dd>&#123;&#123; user.realname|e &#125;&#125;</dd>
<dl>Description</dl>
<dd>&#123;&#123; user.description &#125;&#125;</dd>
</dl>
&#123;% endcall %&#125;

表达式

比较

  • ==/!=
  • and/or/not
  • is/in/|(应用过滤器)/()调用一个可调用对象/
    算数运算

典型示例

导入模板并使用模板中的宏,如下是被导入的forms.html

1
2
3
4
5
6
7
8
&#123;% macro input(name, value='', type='text') -%&#125;
<input type="&#123;&#123; type &#125;&#125;" value="&#123;&#123; value|e &#125;&#125;" name="&#123;&#123; name &#125;&#125;">
&#123;%- endmacro %&#125;
&#123;%- macro textarea(name, value='', rows=10, cols=40) -%&#125;
<textarea name="&#123;&#123; name &#125;&#125;" rows="&#123;&#123; rows &#125;&#125;" cols="&#123;&#123; cols
&#125;&#125;">&#123;&#123; value|e &#125;&#125;</textarea>
&#123;%- endmacro %&#125;

可以这样使用

1
2
3
4
5
6
7
8
&#123;% import 'forms.html' as forms %&#125;
<dl>
<dt>Username</dt>
<dd>&#123;&#123; forms.input('username') &#125;&#125;</dd>
<dt>Password</dt>
<dd>&#123;&#123; forms.input('password', type='password') &#125;&#125;</dd>
</dl>
<p>&#123;&#123; forms.textarea('comment') &#125;&#125;</p>

还可以导入到命名空间中

1
2
3
4
5
6
7
8
&#123;% from 'forms.html' import input as input_field, textarea %&#125;
<dl>
<dt>Username</dt>
<dd>&#123;&#123; input_field('username') &#125;&#125;</dd>
<dt>Password</dt>
<dd>&#123;&#123; input_field('password', type='password') &#125;&#125;</dd>
</dl>
<p>&#123;&#123; textarea('comment') &#125;&#125;</p>


基本是W3Cschool的学习笔记,大部分是复制粘贴的,相当于读写一遍,侵删,以上