Jinja2
jinja模版只是一个文本文件,jinja可以将模版生成任何基于文本的格式,如html,jinja模版并不需要一个特定的扩展名:.html,.xml等都可以,模版中包含的变量或者表达式,当模版被渲染时,会被替换成值和标签,并且可以在模版中控制逻辑。
简单例子:
1 |
|
2 | <html lang='en'> |
3 | <head> |
4 | <title>My Web</title> |
5 | </head> |
6 | <body> |
7 | <ul id="navigation"> |
8 | {% for item in navigation %} |
9 | <li><a href="{{ item.href }}">{{ item.caption }}</a></li> |
10 | {% endfor %} |
11 | </ul> |
12 | <h1>My Web</h1> |
13 | {{ a_variable }} |
14 | {# a comment #} |
15 | </body> |
16 | </html> |
一些默认的jinja分隔符配置:
1 | {% ... %} 用于声明表达式 |
2 | {{ ... }} 将表达式打印到模板输出 |
3 | {# ... #} 模版注释 |
变量
模板变量由传递给模板的上下文字典定义。应用程序将变量传入模板,则可以使用模板变量,变量的可用属性很大程度上取决于提供变量的应用程序。
1 | {{ foo.bar }} |
2 | {{ foo['bar'] }} |
这两个变量代表的是同一个意思,这里要知道的是双花括号不是变量的一部分,可以理解为print语句,如果一个变量或者属性不存在,将得到一个未定义的值,默认行为打印空白或者迭代空字符串。
上面的变量可以理解做了以下步骤:
- 检查foo上是否有bar的属性 getattr(foo,’bar’)
- 如果没有,检查foo中是否有item bar foo.getitem(‘bar’)
- 如果没有,返回一个未定义的对象
过滤器
变量可以通过过滤器进行修改,过滤器通过管道符号(|)与变量分开,并且可以在括号中包含可选参数。可以链接多个过滤器,一个过滤器的输出应用与下一个。
内置过滤列表:
abs(number) 返回绝对值
1
from jinja2 import template
2
template=Template('{{ A | abs }}')
3
template.render(A=-1.1)
attr(obj,name) 获取对象属性
1
def A():
2
'A Template'
3
pass
4
5
template=Template('{{ A | attr("__doc__") }}')
6
template.render(A=A)
batch(value,linecount,fill_with=None) 过滤器批量方法,用于填充缺失的item
1
<table>
2
{%- for row in items|batch(3, ' ') %}
3
<tr>
4
{%- for column in row %}
5
<td>{{ column }}</td>
6
{%- endfor %}
7
</tr>
8
{%- endfor %}
9
</table>
capitalize(s) 首字母大写
1
template=Template('{{A | capitalize}}')
2
template.render(A='jusene')
center(value,width=80) 居中
1
template=Template('{{A | center(20)}}')
2
template.render(A='jusene')
default(value,default_value=’u’,boolean=Flase) 设定默认值,别名d
1
template=Template('{{A | default("A is not defined")}}')
2
template.render(A=A)
如果bealean为Flase,那么第二个变量必须为true:
sring is empty
dictsort(value,case_sensitive=False,by=’key’) 按照键值对排序
1
{% for item in mdict | dictsort %} #按键排序,不区分大小写
2
{% for item in mdict | dictsort(true) %} #按键,区分大小写
3
{% for item in mdict | dictsort(false,'value') %} #按值排序,不区分大小写
escape(s) 将字符串&,<,>,’和”等字符转换为html安全序列,如果需要在html中显示这些包含字符的文本,使用此项就可以完成,别名e
1
template=Template('{{A | escape}}')
2
template.render(A='<a href>')
filesizeformat(value,binary=False) 格式化此值人类友好,如果第二个参数为True,则使用二进制。
1
template=Template('{{A | filesizeformat}}')
2
template.render(A=100000000)
first(seq) 返回序列中的第一项
float(value,default=0.0) 转换一个值为浮点型,如果无法转换将返回0.0
forceescape(value) 执行html转义,这可能将是加倍的转义变量
1
template=Template('{{A | forceescape}}')
2
template.render(A='<a href=>')
format(value,args,*kwargs) 应用python字符格式化
1
template=Template('{{"%s - %s" | format("hello","jinja2")}}')
2
template.render()
groupby(value,attribute) 按照相同的属性分组序列
1
<ul>
2
{% for group in persons | groupby('gender') %}
3
<li>{{ group.grouper }}<ul>
4
{% for person in group.list %}
5
<li>{{ person.first_name }} {{ person.last_name }}</li>
6
{% endfor %}</ul></li>
7
{% endfor %}
8
</ul>
1
<ul>
2
{% for grouper,list in persons|groupby('gender') %}
3
...
4
{% endfor %}
5
</ul>
indent(s,width=4,first=False,blank=False,indentfirst=None) 返回字符串的副本,每行缩进4个空格。第一行和空白行默认没有缩进。
1
template=Template('{{A | indent}}')
2
template.render(A=text)
width: 缩进空格数
first: 默认跳过缩进第一行
blank: 默认跳过缩进空行int(value,default=0,base=10) 该值默认转化十进制整数,如果转换不成功,返回0,base=2二进制转换,base=8八进制转换,base=16十六进制转换。
join(value,d=””,attribute=None) 返回序列中字符串连接的字符串
last(seq) 返回一个序列的最后一个项目
length(object) 返回序列或映射的项目数,别名count
list(value) 将该值转换为列表。如果它是一个字符串,返回的列表将是一个字符列表
lower(s) 转换为小写
map() 对一个序列应用过滤器查找属性,基本用法是映射一个属性:
1
{{title | map(attribute='username') | join(',')}}
或者,可以通过传递过滤器名称和参数来调用过滤器:
1
{{title | map('lower') | join(',')}}
max(value,case_sensitive=False,attribute=None) 从序列中返回最大的项目
case_sensitive 区分大小写min(value,case_sensitive=False,attribute=None) 从序列中返回最小的项目
pprint(value,verbose=False) 优雅的打印出一个变量
random(seq) 从序列中返回一个随机项目
reject() 对每个对象测试来过滤对象,并在测试成功的时候拒绝对象
1
{{ number | reject("odd") }}
rejectattr() 对每个对象的指定属性应用测试,并在测试成功拒绝对象筛选对象
1
{{ users | rejectattr("is_active") }}
replace(s,old,new,count=None) 替换字符串
1
{{ 'Hello JinJa2' | replace('Hello','Goodbye') }}
2
--> Goodbye JinJa2
3
{{ 'aaaa' | replace('a','b',count=2)}}
4
--> bbaa
reverse(value) 反转对象或返转迭代器
round(value,precision=0,method=’common’) 将数字四舍五入到给定精度,第一个参数指定精度(默认0),第二个舍人方法
1
{{42.55 | round}}
2
--> 43.0
3
{{42.55 | round(1,'floor')}}
4
--> 42.5
5
{{42.55 | round(1,'ceil')}}
6
--> 42.6
common 向上向下浮动,分界为5,5(包括5)舍,5以上进1位
ceil 总是向上浮动
floor 总是向下浮动safe(value) 将该值标记为安全,这意味着启用自定义转义的环境,此变量不会被转义。
select() 通过对每个对象的测试来过滤对象,选择成功的对象
selectattr() 过对每个对象的特定的属性测试来过滤对象,选择成功的对象
slice(value,slices.fill_with=None) 切片迭代器并返回包含这些项目的列表的列表
1
<div class="columnwrapper">
2
{%- for column in items|slice(3) %}
3
<ul class="column-{{ loop.index }}">
4
{%- for item in column %}
5
<li>{{ item }}</li>
6
{%- endfor %}
7
</ul>
8
{%- endfor %}
9
</div>
sort(value,reverse=False,case_sensitive=False,attribute=None) 排序一个迭代序列
1
{% for item in iterable| sort %}
2
...
3
{% endfor %}
string(object) 如果还没有,则创建一个字符串unicode。这样一个标记字符串不会被转换回unicode。
sum(iterable,attribute=None,start=0) 返回一系列数字加上参数start的的值(默认0)的和
title(s) 返回值的标题版本,即单词都以大写字母开头。
tojson(value,indet=None) 将结构转储到json,以便在
<script>
标记中使用是安全的。trim(value) 去掉前导和尾随的空白
truncate(s,length=255,killwords=False,end=’…’,leeway=None) 返回字符串的截断副本,第一参数指定默认的长度,第二个参数是指定截断字符,如果文本被截断,会附上end的参数,第四个参数给出的公差余量超过长度的字符串将不会被截断。
unique(value,case_sensitive=False,attribute=None) 去重
upper(s) 转换大写
urlencode(value) 转化用于url的字符串(使用utf-8编码)
urlize(value,trim_url_limit=None,nofollow=False,target=None,rel=None) 将纯文本的url转换为可点击的链接
1
template=Template('{{A | urlize(40,true)}}')
2
print(template.render(A='http://www.baidu.com'))
3
<a href="http://www.baidu.com" rel="nofollow noopener">http://www.baidu.com</a>
4
5
template=Template('{{A | urlize(40,target="_blank")}}')
6
print(template.render(A='http://www.baidu.com'))
7
<a href="http://www.baidu.com" rel="noopener" target="_blank">http://www.baidu.com</a>
wordcount(s) 计算该字符串中的单词
wordwrap(s,width=79,break_long_words=True,wrapstring=None)
xmlattr(d,autospace=True) 根据字典中的项目创建一个SGML / XML属性字符串。所有既非none又undefined的值自动转义:
1
<ul{{ {'class': 'my_list', 'missing': none,
2
'id': 'list-%d'|format(variable)}|xmlattr }}>
3
...
4
</ul>
result
1
<ul class="my_list" id="list-42">
2
...
3
</ul>
没有合适的过滤器,那就自己写过滤器,Flask代码:
1
def double_step_filter(arg):
2
return arg[::2]
3
4
app.add_template_filter(double_step_filter,'double_step')
1
<p>{{ [1,2,3,4,5] | double_step }}</p> {# [1,3,5] #}
Flask还提供了添加过滤器的装饰器“template_filter”,使用起来更加简单:
1
2
def sub(l,start,end):
3
return l[start:end]
1
<p>{{ [1,2,3,4,5] | sub(1,4) }}</p> {# [2,3,4] #}
Flask添加过滤器的方法实际上是封装了对jinja2环境变量的操作:
1
app.jinja_env.filters['sub']=sub
测试
要测试一个变量或表达式,你要在变量后加上一个is以及测试的名称,例如,要得到一个值是否定义过,你可以用name is defined
等,以下内置测试清单:
- callable(object) 返回对象是否可调用
- defined(value) 如果变量已被定义,则返回true
1
{% if variable is defined %}
2
value of variable: {{ variable }}
3
{% else %}
4
variable is not defined
5
{% endif %}
- divisibleby(value,num) 检查一个变量是否可以被数字整数
- eq(a,b) 等于,别名(==),equalto
- escaped(value) 检查值是否被转义
- even(value) 如果变量是偶数,则返回true
- ge(a,b) 大于等于,别名(>=)
- gt(a,b) 大于,别名greaterthan
- in(value,seq) 检查值是否在seq
- iterable(value) 检查是否可以迭代
- le(a,b) 小于等于,别名(<=)
- lower(value) 变量小写,返回true
- lt(a,b) 小于,别名(<)
- mapping(value) 对象是可映射的(字典等),则返回true
- ne(a,b) 不等于,别名(!=)
- none(value) 如果变量为空,返回true
- number(value) 如果变量是数字,则返回true
- odd(value) 如果变量是奇数,则返回true
- sameas(value,other) 检查一个对象是否指向与另一个对象相同的内存地址
1
{% if foo.attribute is sameas false %}
2
the foo attribute really is the 'False' singleton
3
<% endif %>
- sequence(value) 如果变量是一个序列,则返回true
- string(value) 如果对象是字符串,则返回true
- undefined(value) 就像defined()相反
- upper(value) 变量是大写,就返回true
没有合适的测试器,自己动手写,Flask 代码:我们定义一个has_number函数,用正则来判断输入参数是否包含数字,然后调用‘app.add_template_test’方法,第一个是测试器函数,第二个是测试器名称。1
import re
2
def has_number(str):
3
return re.match(r'.*\d+',str)
4
app.add_template_test(has_number,'contain_number')
同过滤器一样,Flask提供添加测试器的装饰器‘template_test’:1
{% if name is contain_number %}
2
<h2>{{ name }} contains number.</h2>
3
{% endif %}
1
2
def end_with(str,suffix):
3
return str.lower().endwith(suffix.lower())
Flask添加测试器的方法是封装了对jinja2环境变量的操作:1
{% if name is end_with 'me' %}
2
<h2>{{ name }} ends with 'me'</h2>
3
{% endif %}
1
app.jinja_env.tests['end_with']=end_with
注释
要把模版中一行的部分注释掉,默认使用注释语法。这在调试或者添加给自己或其他设计者的信息时是有用的:
1 | {# note: disable template because we do not us it |
2 | {% for user in users %} |
3 | ... |
4 | {% endfor %} |
5 | #) |
空白控制
默认配置中,模版引擎不会对空白做进一步修改,所以每个空白都会原封不动返回,我们可以手动剥离模版中的空白,当在块(比如一个for标签、一段注释或者变量表达式)的开始或结束放置一个减号(-),可以移除块前块后的空白:
1 | {% for item in seq -%} |
2 | {{ item }} |
3 | {{%- endfor %}} |
如果开启行语句,他们会自动去除行首的空白。注意:标签和减号之间不能有空白。
转义
让jinja忽略,不要把它作为变量或块来处理,最简单的方法:
1 | {{ '{{' }} |
对于较大的段,落标记一个块为raw:
1 | {% raw %} |
2 | <ul> |
3 | {% for item in seq %} |
4 | <li>{{ item }}</li> |
5 | {% endfor %} |
6 | </ul> |
7 | {% endraw %} |
行语句
如果启用了行语句,就可以把一个标记为一个语句,例如如果行语句前缀配置为#:
1 | <ul> |
2 | # for item in seq: |
3 | <li>{{ item }}</li> ## this comment is ignored |
4 | # endfor |
5 | </ul> |
模版继承
jinja中最强大的部分是模版继承。模版继承允许你构建一个包含你站点共同元素的基本模版‘骨架’,并定义子模版可以覆盖的块。
基本模版
1 |
|
2 | <html lang="en"> |
3 | <html xmlns="http://www.w3.org/1999/xhtml"> |
4 | <head> |
5 | {% block head %} |
6 | <link rel="stylesheet" href="style.css" /> |
7 | <title>{% block title %}{% endblock %} - My Webpage</title> |
8 | {% endblock %} |
9 | </head> |
10 | <body> |
11 | <div id="content">{% block content %}{% endblock %}</div> |
12 | <div id="footer"> |
13 | {% block footer %} |
14 | ©Copyright 2017 by <a href="http://www.jusene.me">you</a> |
15 | {% endblock %} |
16 | </div> |
17 | </body> |
block标签定义了四个字幕版可以填充的块,所有block标签告诉模版引擎子模版可以覆盖模板中定义的这些部分。
子模版
1 | {% extends "base.html" %} |
2 | {% block title %}Index{% endblock %} |
3 | {% block head %} |
4 | {{ super() }} |
5 | <style type=text/css> |
6 | .important { color: #336699} |
7 | </style> |
8 | {% endblock %} |
9 | {% block content %} |
10 | <h1>Index</h1> |
11 | <p class="important"> |
12 | welcome |
13 | </p> |
14 | {% endblock %} |
extend标签是这里的关键,他告诉模版‘继承’另一个模版,当模版系统对这个模版求值时,首先定位父模版。extends标签应该是模版中的第一个标。
不可以在同一个模板中定义多个同名的block标签。因为块标签以两种方向工作,所以存在这种限制,即一个标签不仅提供一个可以填补的部分也
如果你想要多次打印一个块:
1 | <title>{% block title %}{% endblock %}</title> |
2 | <h1>{{ self.title() }}</h1> |
3 | {% block body %}{% endblock %} |
super块
可以调用super来渲染父级别的内容。
1 | {% block sidebar %} |
2 | <h3>Table Of Contents</h3> |
3 | ... |
4 | {{ super() }} |
5 | <% endblock %> |
命名结束标签
jinja2允许在块的结束标签中加入的名称来改善可读性:
1 | {% block sidebar %} |
2 | {% block inner_sidebar %} |
3 | ... |
4 | {% endblock inner_sidebar %} |
5 | {% endblock sidebar %} |
嵌套块和作用域
嵌套块可以胜任更复杂的布局。而默认的块不允许访问块外作用域的变量:
1 | {% for item in seq %} |
2 | <li>{% block loop_item %}{{ item }}{% endblock %}</li> |
3 | {% endfor %} |
这个例子看起来没有问题,但是li是空的,因为item是不可用的,其原因是,如果块被子模块替换,变量在其块中可能是未定义的或者未被传递到上下文。
从jinja2.2开始,可以显式的指定块中的可用变量,只需在块声明中添加scoped修饰:
1 | {% for item in sea %} |
2 | <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li> |
3 | {% endfor %} |
HTML转义
当模版生成html时,始终有这样的风险,变量包含了影响生成html的字节,有两种方法解决:手动转义和自动转义
手动转义
如果启用手动转义,按需转义变量就是自己的责任,变量可能包含>、<、&
等必须转义,
自动转义
当启动自动转义,默认会转义一切,除非值被显示地标记为安全,。
1 | @app.route('/hello') |
2 | def hello(): |
3 | name='<em>World</em>' |
4 | return render_template('hello.html',name=name) |
会返回Hello <em>World</em>
,flask默认开启了自动转义,为了防止html语法注入,我们可以关闭自动转义:
1 | {% autoescape false %} |
2 | <h1>Hello {{ name }}</h1> |
3 | {% autoescape %} |
使用”autoescape”开关前要启用”jinja2.ext.autoescape”扩展,在Flask框架中,这个扩展默认已启用。
控制模版结构
For
遍历序列中的每项。
1 | <h1>Members</h1> |
2 | <ul> |
3 | {% for user in users %} |
4 | <li>{{ user.username|e }}</li> |
5 | {% else %} |
6 | <li>None</li> |
7 | {% endfor %} |
8 | </ul> |
因为模板的变量保留他们的对象属性,可以迭代像dict的容器:
1 | <ul> |
2 | {% for key,value in my_dict.iteritems() %} |
3 | <li>{{ key|e }}</li> |
4 | <li>{{ value|e }}</li> |
5 | {% endfor %} |
6 | </ul> |
在一个for循环块中可以访问这些特殊变量:
- loop.index 当前循环迭代的次数(从1开始)
- loop.index0 当前循环迭代的次数(从0开始)
- loop.revindex 到循环结束需要迭代的次数(从1开始)
- loop.revindex0 到循环结束需要迭代的次数(从0开始)
- loop.first 如果是第一次迭代,为true
- loop.last 如果是最后一次迭代,为true
- loop.length 序列中的项目数
- loop.cycle 在一串序列间取值的辅助函数
loop.cycle辅助函数,伴随循环在一个字符串/变量列表中周期取值:
1 | {% for row in rows %} |
2 | <li class="{{ loop.cycle('odd','even') }}">{{ row }}</li> |
3 | {% endfor %} |
在jinja2.1开始,一个额外的cycle辅助函数允许循环限定外的周期取值。
与python不同,模版的循环不能break或continue。但你可以在迭代过滤序列来跳过项目。
1 | {% for user in users if not user.hidden %} |
2 | <li>{{ user.username|e }}</li> |
3 | {% endfor %} |
下面的例子用递归循环实现了站点地图:
1 | <ul class="sitemap"> |
2 | {%- for item in sitemap recursive %} |
3 | <li><a href="{{ item.href|e }}">{{ item.title }}</a> |
4 | {%- if item.children -%} |
5 | <ul class="submenu">{{ loop(item.children) }}</ul> |
6 | {%- endif %}</li> |
7 | {%- endfor %} |
8 | </ul> |
If
1 | {% if kenny.sick %} |
2 | Kenny is sick. |
3 | {% elif kenny.dead %} |
4 | You killed Kenny! You bastard!!! |
5 | {% else %} |
6 | Kenny looks okay --- so far |
7 | {% endif %} |
宏
宏类似常规编程语言中的函数,他们用于把常用的行为作为可重用的函数,取消手动重复的工作。
1 | {% macro input(name,value='',type='text',size=20) -%} |
2 | <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}"> |
3 | {%- endmacro %} |
在命名空间中,宏之后可以像函数一样调用:
1 | <p>{{ input('username') }}</p> |
2 | <p>{{ input('password'),type='password' }}</p> |
在宏的内部有三个特殊变量:
- varargs 如果有多于宏接受的参数个数的位置参数被传入,它们会作为列表的值保存在varargs变量上
- kwargs 同kwargs,但只针对关键字参数。所以未使用的关键字参数会存储在这个特殊变量中
- caller 如果宏通过call标签调用,调用者会作为可调用的宏被存储在这个变量中
宏也可以暴露某些内部细节。下面的宏对象属性是可用的:
- name 宏的名称,会打印input
- arguments 一个宏接受的参数名的元组
- defaults 默认值的元组
- catch_kwargs 如果宏接受额外的关键字参数(也就是访问特殊的kwargs变量),为true
- catch_varargs 如果宏接受额外的位置参数(也就是访问特殊的varargs变量),为true
- caller 如果宏接受特殊的caller变量且call标签调用,为true
调用
需要把一个宏传递到另一个宏。为此,可以使用特殊的call块:
1 | {% macro render_dialog(title,class='dialog') -%} |
2 | <div class="{{ class }}"> |
3 | <h2>{{ title }}</h2> |
4 | <div class="contents"> |
5 | {{ caller() }} |
6 | </div> |
7 | </div> |
8 | {%- endmacro %} |
9 | {% call render_dialog("Hello World") %} |
10 | This is a simple dialog |
11 | {% endcall %} |
也可以向调用块传递参数:
1 | {% macro dump_users(users) -%} |
2 | <ul> |
3 | {%- for user in users %} |
4 | <li><p>{{ user.username|e }}</p>{{ caller(user) }}<l/i> |
5 | {%- endfor %} |
6 | </ul> |
7 | {%- endmacro %} |
8 | |
9 | {% call(user) dump_users(list_of_user) %} |
10 | <dl> |
11 | <dl>Realname</dl> |
12 | <dd>{{ user.realname|e }}</dd> |
13 | <dl>Description</dl> |
14 | <dd>{{ user.description }}</dd> |
15 | </dl> |
16 | {% endcall %} |
17 | ``` |
18 | |
19 | #### 过滤器 |
20 | |
21 | 过滤器段允许你在一块模板数据上应用常规 Jinja2 过滤器。只需要把代码用 filter 节包裹起来: |
1 | |
2 | #### 赋值 |
3 | |
4 | 在代码块中,你也可以为变量赋值: |
5 | |
6 | ```html |
7 | {% set navigation = [('index.html','Index'),('about.html','About')] %} |
8 | {% set key,value = call_something() %} |
包含
include语句用于包含模版,并在当前命名空间返回那个文件的内容渲染结果:
1 | {% include 'header.html' %} |
2 | Body |
3 | {% include 'footer.html' %} |
从jinja 2.2开始,可以把一句include用ignore missing标记,这样如果模板不存在,jinja会忽略这条语句,当与with或without context语句联合使,用必须被放在上下文可见性语句之前:
1 | {% include "sidebar.html" ignore missing %} |
2 | {% include "sidebar.html" ignore missing with context %} |
3 | {% include "sidebar.html" ignore missing without context %} |
如果提供一个模板列表,它会在包含前被检查是否存在。第一个存在的模板会被包含进来,如果给出ignoremissing,且所以模板都不存在,不会做任何渲染,否则将会抛出异常。
1 | {% include ['page_detailed.html','page.html'] %} |
2 | {% include ['special_sidebar.html','sidebar.html'] ignore missing %} |
导入
jinja2支持在宏中放置经常使用的代码,这些宏可以被宏导入,并在不同的模板中使用,导入的会被缓存,并且默认导入的模板不能访问当前模板中非全局变量。
一个渲染表单宏(forms.html):
1 | {% macro input(name,value='',type='text') -%} |
2 | <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}"> |
3 | {%- endmacro %} |
4 | |
5 | {%- macro textarea(name,value='',rows=10,cols=40) -%} |
6 | <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value|e }}</textarea> |
7 | {%- endmacro %} |
最简单灵活的方式把模版导入为一个变量:
1 | {% import 'forms.html' as forms %} |
2 | <dl> |
3 | <dt>Username</dt> |
4 | <dd>{{ forms.input('username') }}</dd> |
5 | <dt>Password</dt> |
6 | <dd>{{ forms.input('password') }}</dd> |
7 | </dl> |
8 | <p>{{ forms.textarea('comment') }}</p> |
此外你也可以从模板导入名称到当前的命名空间:
1 | {% from 'forms.html' import input as input_field,textarea %} |
2 | <dl> |
3 | <dt>Username</dt> |
4 | <dd>{{ input_field('username') }}</dd> |
5 | <dt>Password</dt> |
6 | <dd>{{ input_field('password',type='password') }}</dd> |
7 | </dl> |
8 | <p>{{ textarea('comment') }}</p> |
名称以一个或更多下划线开始的宏和变量是私用的,不能被导入。
导入上下文行文
默认下,每个包含的模板会被传递到当前上下文,而导入的模板不会。我们可以显式的更改,通过在import/include声明中直接添加with context或without context。
1 | {% from 'forms.html' import input with context %} |
2 | {% include 'header.html' without context %} |
表达式
jinja中允许使用基本表达式,就像python一样工作。
字面量
- ‘string’ 双引号或单引号中间的一切都是字符串
- 42/42.23 直接写下数值就可以创建整数和浮点数
- [‘list’,’object’] 一对中括号起来的就是列表,列表用于存储和迭代序列化的数据:
1
<ul>
2
{% for href,caption in [('index.html','Index'),('about.html','About')] %}
3
<li><a href="{{ href }}">{{ caption }}</a></li>
4
{% endfor %}
5
</ul>
- (‘tuple’,’of’,’values’) 元组和列表类似,只是你不能修改元组。如果元组只有一个项,需要逗号结尾它。
- {‘dict’:’of’,’key’:’and’,’value’:’pairs’} 字典在模板很少使用
- true/false 使用小写
算术
jinja允许数学运算,在模板很少使用,但确实是可以使用的:
1 | + {{ 1+1 }} |
2 | - {{ 3-2 }} |
3 | / {{ 1/2 }} = 0.5 |
4 | // {{ 20//7 }} = 2 |
5 | % {{ 11%7 }} |
6 | * {{ 2*2 }} = 4 {{ '=' * 80 }} 80个= |
7 | ** {{ 2**3 }} |
比较
1 | == 比较两个对象是否相等。 |
2 | != 比较两个对象是否不等。 |
3 | > 如果左边大于右边,返回 true 。 |
4 | >= 如果左边大于等于右边,返回 true 。 |
5 | < 如果左边小于右边,返回 true 。 |
6 | <= 如果左边小于等于右边,返回 true 。 |
逻辑
1 | and 如果左操作数和右操作数同为真,返回 true 。 |
2 | or 如果左操作数和右操作数有一个为真,返回 true 。 |
3 | not 对一个表达式取反。 |
4 | (expr) 表达式组。 |
其他运算符
1 | in 运行序列/映射包含检查 |
2 | is 运行一个测试 |
3 | | 应用一个过滤器 |
4 | ~ 把所以的操作数转换为字符串,并连接它。{{ 'Hello' ~ name ~ '!' }}会返(name值为jinja) Hello jinja ! |
5 | () 调用一个调用量: {{ post.render() }}。在圆括号中,可以像python一样使用位置参数和关键字参数 |
6 | [] 获取一个变量的属性 |
if表达式
1 | {% extends layout_template if layout_template is defined else 'master.html' %} |
一般的语法{do something} if {something is true} else {do something}
全局函数清单
默认下。下面的函数在全局作用与中可用:
- range([start],stop[,step])
- lipsum(n=5,html=True,min=20,max=100) 在模板中生成lorem ipsum乱数假文。默认会生成5段HTML,每段在20到100词之间
- dict(**items) 字典字面量的替代品,{‘foo’:’bar’}与dict(foo=bar)等价
- class cycler(*items) 周期计允许你在若干值中循,环类似loop.cycle的工作模式。不同于loop.cycle的是,无论如何都可以在循环外或在多重循环中使用它。
1
{% set row_class=cycler('odd','even') %}
2
<ul class='browser'>
3
{% for folder in folder %}
4
<li class="foler {{ row_class.next() }}">{{ folder|e }}</li>
5
{% endfor %}
6
{% for filename in files %}
7
<li class="file {{ row_class.next() }}">{{ filename|e }}</li>
8
{% endfor %}
9
</ul>
- reset() 重置周期计到第一项
- next() 返回当前项并跳转到下一个
- current() 放回当前项
扩展
with语句
类似于Python中的”with”关键字,它可以限制with语句块内对象的作用域:
1 | {% with foo = 1 %} |
2 | {% set bar = 2 %} |
3 | {{ foo + bar }} |
4 | {% endwith %} |
使用”with”关键字前要启用”jinja2.ext.with_”扩展,在Flask框架中,这个扩展默认已启用。
1 | {% with arr = ['Sunny'] %} |
2 | {% arr.append('Rainy') %} |
3 | {{ arr }} |
4 | {% endwith %} |
这时使用”arr.append(‘Rainy’)”页面会输出”None”,换成%%来执行,程序会报错,因为这是个表达式,不是语句。
我们可以启用”jinja2.ext.do”扩展。然后在模板中执行”do”语句即可:
1 | {% with arr = ['Sunny'] %} |
2 | {{ do arr.append('Rainy') }} |
3 | {{ arr }} |
4 | {% endwith %} |