jinja2是python中的一个优秀的模板语言,类似于django的模板。它的速度快,安全,目前被各种框架被广泛使用。
官网地址:http://jinja.pocoo.org/
它的一些特性:
沙箱执行机制很安全 通过对HTML进行自动转义防止XSS攻击 模板继承 实时编译为最优化的python代码,使得它运行速度非常快 可选的模板预编译 容易调试,错误行数直接指向模板中的行 配置文件语法也是模板 安装
非常简单,一条命令搞定:
基本使用
通过创建一个Template类,不过这种方法不是推荐方式:
1
2
3
4
from jinja2 import Template
template = Template ( 'Hello @@ name @@!' )
template . render ( name = 'John Doe' )
u 'Hello John Doe!'
Python3的支持
目前是支持Python3.3或以上版本的,所有单元测试都能通过,但是可能还会有隐藏的小bug。
API
基础
Jinja2使用一个中心对象叫模板环境Environment,用它来存储陪着和全局对象,
然后使用它从文件或其他地方加载模板。就算刚刚的Template例子,一个Environment对象也被隐式的创建了。
大多数框架会在程序启动时创建一个Environment对象,用它来加载模板。
最简单的陪着Jinja2和加载模板的方式类似下面:
1
2
3
from jinja2 import Environment , PackageLoader
env = Environment ( loader = PackageLoader ( 'yourapp' , 'templates' ))
它会创建一个Environment,使用了默认配置和一个在yourapp包的templates文件夹下面搜索模板的加载器。
你还可以使用其他的加载器,或者自定义加载器,来从数据库或其他地方加载模板。
要想加载到一个模板,只需使用get_template()方法
1
2
3
template = env . get_template ( 'mytemplate.html' )
print
template . render ( the = 'variables' , go = 'here' )
Unicode
Jinja2内部使用Unicode,所以传给render方法的参数必须是unicode对象,另外换行符是unix的’\n’,
最好在每个python模块第一行添加
1
# -*- coding: utf-8 -*-
Jinja2的模板默认编码是utf-8,一些库会严格检查str类型比如datetime.strftime,它不接受unicode参数。
所以Jinja2对于ascii字符串返回str,其他的返回unicode,比如:
1
2
3
4
5
m = Template ( u "@ % s et a, b = 'foo', 'föö' %@" ) . module
m . a
'foo'
m . b
u 'f \xf6\xf6 '
自动转换
推荐的做法是开启 Autoescape Extension扩展,下面是一个推荐的启动配置方案,对于.html,.htm和.xml的模板开启自动转换,
对于其他的禁止自动转换:
1
2
3
4
5
6
7
8
9
10
def guess_autoescape ( template_name ):
if template_name is None or '.' not in template_name :
return False
ext = template_name . rsplit ( '.' , 1 )[ 1 ]
return ext in ( 'html' , 'htm' , 'xml' )
env = Environment ( autoescape = guess_autoescape ,
loader = PackageLoader ( 'mypackage' ),
extensions = [ 'jinja2.ext.autoescape' ])
加载器
加载器用来某个源比如文件来加载模板。Environment对象会将编译后的模块放到内存中,
类似于python中的sys.modules,但是不同之处在于如果模板更改了它会自动查询加载。
所有加载器都继承自BaseLoader,如果你想定义自己的加载器,就写个子类,重写它的get_source方法即可。
一个基本的从文件加载模板的加载器像下面这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from jinja2 import BaseLoader , TemplateNotFound
from os.path import join , exists , getmtime
class MyLoader ( BaseLoader ):
def __init__ ( self , path ):
self . path = path
def get_source ( self , environment , template ):
path = join ( self . path , template )
if not exists ( path ):
raise TemplateNotFound ( template )
mtime = getmtime ( path )
with file ( path ) as f :
source = f . read () . decode ( 'utf-8' )
return source , path , lambda : mtime == getmtime ( path )
内置的一些加载器:
jinja2.FileSystemLoader(searchpath, encoding=‘utf-8’, followlinks=False) #文件系统 jinja2.PackageLoader(package_name, package_path=‘templates’, encoding=‘utf-8’) #包 jinja2.DictLoader(mapping) #字典,没啥用 jinja2.FunctionLoader(load_func) #通过一个函数来加载 jinja2.PrefixLoader(mapping, delimiter=’/’) #有前缀的 jinja2.ChoiceLoader(loaders) #多个加载器,找到第一个匹配 jinja2.ModuleLoader(path) #预编译好的模块 自定义过滤器
@% raw %@@@ 42|myfilter(23) @@@% endraw %@会被myfilter(42, 23)调用,一个例子
1
2
def datetimeformat ( value , format = '%H:%M / %d -%m-%Y' ):
return value . strftime ( format )
然后注册到环境中
1
environment . filters [ 'datetimeformat' ] = datetimeformat
模板中使用:
1
2
written on: @@ article.pub_date|datetimeformat @@
publication date: @@ article.pub_date|datetimeformat('%d-%m-%Y') @@
还能给过滤器传入当前模板的context或environment,
所以就有了environmentfilter(), contextfilter() 和evalcontextfilter()三个装饰器。
下面是一个简单例子,将所有文本换行符转换为html换行符,文本段落转换我html段落。如果启动自动转换,返回安全的html字符串:
1
2
3
4
5
6
7
8
9
10
11
12
13
import re
from jinja2 import evalcontextfilter , Markup , escape
_paragraph_re = re . compile ( r '(?:\r\n|\r|\n){2,}' )
@evalcontextfilter
def nl2br ( eval_ctx , value ):
result = u ' \n\n ' . join ( u '<p> %s </p>' % p . replace ( ' \n ' , Markup ( '<br> \n ' ))
for p in _paragraph_re . split ( escape ( value )))
if eval_ctx . autoescape :
result = Markup ( result )
return result
模板语法
介绍完API后,开始介绍模板设计语法
基础
下面是一个典型的模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!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 >
默认的语法配置是这样的,你可以自己自定义:
* @% raw %@@% ... %@@% endraw %@ 这个是语句
* @% raw %@@@ ... @@@% endraw %@ 这个是表达式,打印模板输出
* @% raw %@{# ... #}@% endraw %@ 这个是注释部分,不会输出
* # ... # 这个是行语句
变量
两种访问方式:
1
2
3
4
5
6
7
8
@@ foo.bar @@
@@ foo['bar'] @@
# 过滤器
@@ name|striptags|title @@
# 测试
@% if loop.index is divisibleby(3) %@
空行控制,如果trim_blocks 和 lstrip_blocks都启用,那么模板自己的控制块行将不输出。
行语句
下面等价
1
2
3
4
5
6
7
8
9
10
11
12
< ul >
# for item in seq
< li > @@ item @@</ li >
# endfor
</ ul >
< ul >
@% for item in seq %@
< li > @@ item @@</ li >
@% endfor %@
</ ul >
模板继承
一个基础模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
< html lang = "en" >
< head >
@% block head %@
< link rel = "stylesheet" href = "style.css" />
< title > @% block title %@@% endblock %@ - My Webpage</ title >
@% endblock %@
</ head >
< body >
< div id = "content" > @% block content %@@% endblock %@</ div >
< div id = "footer" >
@% block footer %@
© Copyright 2008 by < a href = "http://domain.invalid/" > you</ a > .
@% endblock %@
</ div >
</ body >
</ html >
一个子模板继承上面的基础模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@% extends "base.html" %@
@% block title %@Index@% endblock %@
@% block head %@
@@ super() @@
< style type = "text/css" >
. important {
color : #336699 ;
}
</ style >
@% endblock %@
@% block content %@
< h1 > Index</ h1 >
< p class = "important" >
Welcome to my awesome homepage.
</ p >
@% endblock %@
还能指定模板名字,更加具有可读性
1
2
3
4
5
@% block sidebar %@
@% block inner_sidebar %@
...
@% endblock inner_sidebar %@
@% endblock sidebar %@
字符转换
可自动转换html字符,也可以手动转换
当使用自动转换时,所有的会被转换,除非你声明它是安全的。有两种方式声明安全:
context字典中指定MarkupSafe.Markup, 模板使用 |safe过滤器 流程控制语句
列表循环
1
2
3
4
5
6
7
8
9
10
11
< h1 > Members</ h1 >
< ul >
@% for user in users %@
< li > @@ user.username|e @@</ li >
@% endfor %@
</ ul >
@% for user in users %@
@%- if loop.index is even %@@% continue %@@% endif %@
...
@% endfor %@
key-value形式的循环:
1
2
3
4
5
6
7
< dl >
@% for key, value in my_dict.iteritems() %@
< dt > @@ key|e @@</ dt >
< dd > @@ value|e @@</ dd >
@% endfor %@
</ dl >
if判断
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 %@
赋值语句
1
2
@% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %@
@% set key, value = call_something() %@
块赋值
1
2
3
4
@ % set navigation % @
< li >< a href = "/" > Index </ a >
< li >< a href = "/downloads" > Downloads </ a >
@ % endset % @
包含模板
1
2
3
4
5
6
@% include 'header.html' %@
@% include "sidebar.html" ignore missing %@
@% include "sidebar.html" ignore missing with context %@
@% include "sidebar.html" ignore missing without context %@
Body
@% include 'footer.html' %@
i18n
国际化例子
1
2
<p>@% trans user=user.username %@Hello @@ user @@!@% endtrans %@</p>
@@ _('Hello %(user)s!')|format(user=user.username) @@
临时指定autoescape
1
2
3
4
5
6
7
@% autoescape true %@
Autoescaping is active within this block
@% endautoescape %@
@% autoescape false %@
Autoescaping is inactive within this block
@% endautoescape %@
jinja2扩展
添加扩展
1
jinja_env = Environment ( extensions = [ 'jinja2.ext.i18n' ])
FAQ
参考 http://jinja.pocoo.org/docs/dev/faq/