>>> from Jinja2 import Template >>> template = Template('Hello {{ name }}!') >>> template.render(name='World') u'Hello World!'这个例子使用字符串作为模板内容创建了一个Template实例,然后用"name='World'"作为参数调用"render方法,将内容中的'name'替换为"World",最终返回渲染过的字符串--"u'Hello World!'"。 API Environment Environment是Jinja2中的一个核心类,它的实例用来保存配置、全局对象,以及从本地文件系统或其它位置加载模板。 多数应用会在初始化时创建Environment实例,然后用它来加载模板。当然,如果系统有必要使用不同的配置,也可以创建多个Environment实例一起使用。 配置Jinja2为你的应用加载模板的最简单的方式可以像下面这样: from Jinja2 import Environment, PackageLoader env = Environment(loader=PackageLoader('yourapplication', 'templates')) 上述代码使用缺省配置创建了一个Environment实例,并指定PackageLoader作为模板加载器。PackageLoader可以从你的python应用程序的包中读取并加载模板。在之后的文档中会逐一介绍Jinja2的加载器。 创建了Environment实例,我们就可以加载模板了: template = env.get_template('mytemplate.html') 之后就可以跟上文中的例子一样用render方法来渲染模板了。 print template.render(the='variables', go='here') 高级API Environment类: class Environment(block_start_string='{%', block_end_string='%}', variable_start_string='{{', vari- able_end_string='}}', comment_start_string='{#', comment_end_string='#}', line_statement_preix=None, trim_blocks=False, extensions=(), optimized=True, undefined= , finalize=None, autoescape=False, loader=None) Environment是Jinja2的核心组件,它包含了重要的共享变量,例如:配置,过滤器,测试器,全局变量等等。Environment 的实例如果没有被共享或者没有加载过模板则可以进行修改,如果在加载过模板之后修改Environment实例会遇到不可知的结果。 参数介绍: loader 模板加载器. block_start_string 块开始标记符,缺省是 '{%'. block_end_string 块结束标记符,缺省是 '%}'. variable_start_string 变量开始标记符,缺省是 '{{'. variable_start_string 变量结束标记符,缺省是 '{{'. comment_start_string 注释开始标记符,缺省是 '{#'. comment_end_string 注释结束标记符,缺省是 '#}'. 通过修改上面几个标记符参数,可以让我们的模板变成另外一种风格,比如 env = Environment( block_start_string="<#", block_end_string="#>, variable_start_string="${", variable_start_string="}", comment_start_string="<#--", comment_end_string="--#>", ...) 这样,我们的模板可以设计为下面的样子: <# block title #> Index <# endblock #> ${name} <#-- this is comment --#> 怎么样,是不是有点像freemarker的风格?但是我们不推荐这样做,否则就无法使用Jinja的编辑器来编辑模板了。 auto_reload 如果设为True,Jinja会在使用Template时检查模板文件的状态,如果模板有修改, 则重新加载模板。如果对性能要求较高,可以将此值设为False。 autoescape XML/HTML自动转义,缺省为false. 就是在渲染模板时自动把变量中的<>&等字符转换为<>&。 cache_size 缓存大小,缺省为50,即如果加载超过50个模板,那么则保留最近使用过多50个模板,其它会被删除。如果换成大小设为0,那么所有模板都会在使用时被重编译。如果不希望清除缓存,可以将此值设为-1. undefined Undefined或者其子类,用来表现模板中未定义的值 使用过freemarker的朋友应该知道,在freemarker中模板中使用值为null的变量时会看到一个“很黄很暴力”的一堆错误栈信息。有些人对freemarker的这种处理方式不以为然,因为这样还需要对变量值加入判断,处理起来比较繁琐。而另一个比较有名气的模板引擎Velocity则会忽略空值,例如在Velocity中打印值为null的变量将会得到一个空字符。 Jinja通过设置不同的undefined参数来得到类似Freemarker或者Velocity的处理方式。 line_statement_prefix 指定行级语句的前缀. extensions Jinja的扩展的列表,可以为导入到路径字符串或者表达式类 Template类 Template类是Jinja的另一个重要的组件,它可以被看作是一个编译过的模板文件,被用来产生目标文本. Template类的构建器参数和Environment类基本相同, 区别是,创建Template实例需要一个模板文本参数,另外它不需要loader参数。 Template实例是一个不可变对象,即你不能修改Template实例的属性。 一般情况下,我们会使用Environment实例来创建Template,但也可以直接使用Template构建器来创建。如果要用构建器来创建Template实例,那么Jinja会根据构建器参数自动为此Template创建/指派一个内部Environment实例,凡是使用相同构建器参数(不包括模板文本串参数)创建的Template实例都会共享同一个内部Environment实例。 方法:
render(*args, **kwargs)此方法接受与“dict”相同的构建器参数:一个dict,dict的子类,或者一些关键字参数。下面两种调用方式是等价的: template.render(knights='that say nih') template.render({'knights': 'that say nih'})
generate(*args, **kwargs)此方法会一段一段的渲染模板,而不是一次性的将整个模板渲染成目标文本。这对产生非常大的模板时非常有用。调用此方法会返回一个产生器(generator),它可以....
stream(*args, **kwargs)与generate功能类似,只不过此方法返回一个TemplateStream module 此方法用来在模板运行时导入, 也可以用来在python代码中访问导出的模板变量. >>> t = Template('{% macro foo() %}42{% endmacro %}23') >>> unicode(t.module) u'23' >>> t.module.foo() u'42'. Undefined Types 未定义类型 Undefined及其子类类被用来作为未定义类型。Environment的构建器可以指定undefined参数,它可以是undefined types中的任意一个,或者是Undefined的子类。当模板引擎无法找到一个名称或者一个属性时,使用的Undefined会决定哪些操作可以正常进行,哪些不可以。 '''class Undefined(hint=None, obj=None, name=None)''' 缺省undefined类型。此未定义类型可以打印或者作为sequence迭代。但是不能做其它操作,否则会抛出UndefinedError
foo = Undefined(name='foo') >>> str(foo) '' >>> not foo True >>> foo + 42 Traceback (most recent call last): ... Jinja2.exceptions.UndefinedError: 'foo' is undefined'''class DebugUndefined(hint=None, obj=None, name=None)'''
>>> foo = DebugUndefined(name='foo') >>> str(foo) '{{ foo }}' >>> not foo True >>> foo + 42 Traceback (most recent call last): ... Jinja2.exceptions.UndefinedError: 'foo' is undefined'''class StrictUndefined(hint=None, obj=None, name=None)'''
>>> foo = StrictUndefined(name='foo') >>> str(foo) Traceback (most recent call last): ... Jinja2.exceptions.UndefinedError: 'foo' is undefined >>> not foo Traceback (most recent call last): ... Jinja2.exceptions.UndefinedError: 'foo' is undefined >>> foo + 42 Traceback (most recent call last): ... Jinja2.exceptions.UndefinedError: 'foo' is undefinedLoaders 加载器 加载器负责从某些位置(比如本地文件系统)中加载模板,并维护在内存中的被编译过的模块。 文件系统加载器,它可以从本地文件系统中查找并加载模板: class FileSystemLoader(searchpath, encoding='utf-8', cache_size=50, auto_reload=True) 第一个参数searchpath是查找路径,它可以是一个路径字符串,也可以是保护多个路径的sequence。 >>> loader = FileSystemLoader('/path/to/templates') >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) 包加载器。它可以从python包中加载模板: class PackageLoader(package_name, package_path='templates', encoding='utf-8', cache_size=50, auto_reload=True) >>> loader = PackageLoader('mypackage', 'views') 字典加载器。在mapping参数中明确指定模板文件名的路径。它用来做单元测试比较有用: class DictLoader(mapping, cache_size=50, auto_reload=False) >>> loader = DictLoader({'index.html': 'source here'}) 函数加载器。让指定的函数来返回模板文件的路径。 class FunctionLoader(load_func, cache_size=50, auto_reload=True) >>> def load_template(name): ... if name == 'index.html' ... return '...' ... >>> loader = FunctionLoader(load_template) 前缀加载。如果你的工程中包含很多应用,那么多应用之间模板名称就可能存在命名冲突的问题。使用前缀加载器可以有效的解决不同应用之间模板命名冲突问题。 class PrefixLoader(mapping, delimiter='/', cache_size=50, auto_reload=True) >>> loader = PrefixLoader({ ... 'app1': PackageLoader('mypackage.app1'), ... 'app2': PackageLoader('mypackage.app2') ... }) 如此,如果要使用app1中的模板,可以get_template('app1/xxx.html'), 使用app2的模板,可以使用get_template('app2/xxx.html')。delimiter字符决定前缀和模板名称之间的分隔符,默认为'/'。 选择加载器,与PrefixLoader类似,可以组合多个加载器。当它在一个子加载器中查找不到模板时,它会在下一个子加载器中继续查找。如果你要用一个不同的位置覆盖内建模板时非常有用 class ChoiceLoader(loaders, cache_size=50, auto_reload=True) >>> loader = ChoiceLoader([ ... FileSystemLoader('/path/to/user/templates'), ... PackageLoader('myapplication') 所有加载都继承自BaseLoader,如果你要实现一个自定义加载可以,可以写一个BaseLoader的子类,并覆盖get_source方法。 class BaseLoader(cache_size=50, auto_reload=True) 一个简单的例子 from Jinja2 import BaseLoader, TemplateNotFound from os.path import join, exists, getmtime class MyLoader(BaseLoader): def __init__(self, path, cache_size=50, auto_reload=True): BaseLoader.__init__(self, cache_size, auto_reload) 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) get_source(environment, template) load(environment, name, globals=None) 加载一个模板。此方法会在缓存中查找模板,如果缓存中不存在则调用get_source得到模板的内容,缓存后返回结果。 注意,BaseLoader已经实现了load方法,它对模板的缓存进行了处理。如果你不需要自己维护缓存,则不必重写此方法。 Utilites 用来帮助你添加自定义过滤器或者函数到Jinja中 environmentfilter(f) contextfilter(f) environmentfunction(f) contextfunction(f) escape(s) class Markup() 异常 类名 描述 class TemplateError() 所有模板异常的基类 class UndefinedError() 操作一个未定义对象时 class TemplateNotFound(name) 模板未找到 class TemplateSyntaxError(message, lineno, name) 模板语法错误 模板设计文档 概述 一个模板其实就是一个普通的文本文件。它可以被设计为任何文本格式(HTML,XML,CSV等等)。它也不需要确定的扩展名,不过一般我们都会用'.html'或'.xml' 模板中包含变量,表达式,标签,变量和表达式会在模板渲染时被用值来替换,标签控制模板的逻辑。Jinja的语法主要参考自Django和python。 下面是一个简单的模板,它包含的了几个模板中的基本元素,在之后的文档中会对这些元素做详细说明。
My Webpage
{{ foo.bar }} {{ foo['bar'] }}注意,上面的'{{ .. }}是Jinja的用来打印变量标记。如果要在其它标签中访问变量,则不能在变量名旁边加花括号。 过滤器(filters) 变量可以在模板中被过滤器修改. 使用过滤器的方式比较类似管道(pipe)操作。如:
'{{ name|striptags|title }}'这个例子的意思是:将name变量用striptags消除变量值中的tag(用<>括起来的内容),再用title过滤器将首字符大写。 过滤器也可以接受参数,用起来比较像调用函数
'{{ list|join(', ') }}'内建过滤器介绍参见内建过滤器一节。 检查器(Tests) 检查器用来在Jinja的if块里面检查一个变量是否符合某种条件。它的用法是 varname is atest, 例如检查一个变量是否存在 {% if name is defined %} 这里, defined就是一个检查器。 检查器跟过滤器一样,也可以有参数,如果检查器只有一个参数,可以不写括号,直接用一个空格将检查器名和参数隔开,如下例中,两行代码的作用是一样的: {% if loop.index is divisibleby 3 %} {% if loop.index is divisibleby(3) %} 在后面的内建检查器列表一节中会介绍各个内建检查器 注释 Jinja中可以加入注释,如: {# note: disabled template because we no longer user this {% for user in users %} ... {% endfor %} #} 这些注释内容不会出现在模板产生的文本中。 模板继承 模板继承是Jinja中一个非常有用的功能。这个功能允许你创建一个包含有所有公共元素的页面基本骨架,在子模板中可以重用这些公用的元素。 使用模板继承其实很简单,下面我们开始用一个例子来介绍模板继承的用法。 基础模板 我们首先写一个名为"base.html"的模板,它包含下面的内容:
{% block head %} {% block title %}{% endblock %} - My Webpage {% endblock %}在这个模板中有很多'block', 这些block中间的内容,我们将会在子模板中用其它内容替换。 子模板 我们再写一个名为"child.html"的模板,内容如下:{% block content %}{% endblock %}
{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} {% endblock %} {% block content %}:在这个模板的第一行,我们用{% extends "base.html" %}标明,这个模板将继承base.html. 在随后的内容中包含了很多跟base.html中相同的block,如title,content,这些block中的内容将会替换base.html的内容后输出. :extends后面的模板名称的写法依赖于此模板使用的模板加载器, 比如如果要使用FileSystemLoader,你可以在模板文件名中加入文件的文件夹名,如:Index
Welcome on my awsome homepage.
{% endblock %}
{% extends "layout/default.html" %}在base.html中,我们定义了block “footer”,这个block在子模板中没有被重定义,那么Jinja会直接使用父模板中的内容输出。 另外要注意,在同一个模板中不能定义名称相同的block。 如果你要在模板中多次打印同一个block,可以用用self变量加上block的名字:
{% block title %}{% endblock %}和Python不同的地方是,Jinja不支持多继承。 super block 如果要在子模板中重写父模板的block中打印被重写的block的内容,可以调用super关键字。{{ self.title() }}
{% block body %}{% endblock %}
{% block sidebar %}Table Of Contents
... {{ super() }} {% endblock %}HTML转义 :我们传递给模板的变量中可能会有一些html标记符,这些标记符也许会影响我们页面的正常显示,而且会给我们的站点带来跨站脚本攻击的隐患。 Jinja提供了两种方式-自动或者手工来对变量值进行html转义,即把'<'转换为'<','>'转换为'>','&'转换为'&' 通过给Environment或Template的构建器传递autoescape参数,可以设置自动转义与否。 手动转义 这种方式需要我们使用过滤器转换我们需要转义的变量 '{{ user.username|e }}'. 这里'e'就是转义过滤器 自动转义 这种方式会在打印变量时自动进行转义。除非使用'safe'过滤器标明不需要转义:'{{ user.username|safe }}'.结构控制标记 Jinja中的控制标记包括:条件判断标记(if/elif/else),循环控制(for-loop),另外还有macro(宏)和上文中提到的block。 for 循环打印一个序列,例如:Members
{% for row in rows %}
{% for user in users if not user.hidden %}
{% if users %}
{% if kenny.sick %} Kenny is sick. {% elif kenny.dead %} You killed Kenny! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}if语句也可以被用来做内联表达式或者for语句过滤器。 宏(Macro) 宏的作用和函数比较类似。用来把一部分常用的代码封装起来,避免重复性的工作。 宏可以定义在一个帮助性质的模板中,用imported的方式被其它模板引用;也可以在模板中定义并直接使用。这两种方式有个显著的不同:在模板中定义的宏可以访问传给模板的上下文变量;在其它模板中定义的宏则只能访问到传递给它的变量,或者全局变量。 这里有个打印表单元素的简单的宏
{% macro input(name, value='', type='text', size=20) -%} {%- endmacro %}这个宏可以在命名空间中被直接调用
如果这个宏在其它模板中,你必须先用import引入。 在一个模板中你可以访问三种特殊变量: *'''varargs''' 等同于python语法中的"*args" *'''kwargs''' 等同于python语法中的"**kwargs" *'''caller''' 被call标签调用的宏,调用者会被存储在一个叫做caller的变量中。 宏其实也是一个对象,它有一些属性可以在模板中使用: *'''name''' 宏的名称。{{ 'input.name':string }} *'''arguments''' 宏可以接受的参数,这个属性是一个元组 *'''defaults''' 缺省值的元组 *'''catch_kwargs''' 这个宏是否可以接受关键字参数 *'''catch_varargs''' 这个宏是否可以接受索引位置参数 *'''caller''' 是否有caller变量,可以被call标签调用 Call 在某些情况下,你可能需要将一个宏对象传递到另外一个宏中使用。为了实现此目的,你可以使用call block。{{ input('username') }}
{{ input('password', type='password') }}
{% macro render_dialog(title, class='dialog') -%}在这里例子里,我们用"call render_dialog"调用了宏render_dialog,其中,'hello world作为render_dialog的title参数。在render_dialog中用{{ caller() }}将 call block中的内容显示出来。 在使用 {{ caller() }} 时,也可以传入参数,如下例:{%- endmacro %} {% call render_dialog('Hello World') %} This is a simple dialog rendered by using a macro and a call block. {% endcall %}{{ title }}
{{ caller() }}
{% macro dump_users(users) -%}
{{ user.username|e }}
{{ caller(user) }}{% navigation = [('index.html', 'Index'), ('about.html', 'About')] %}include 用include可以导入另外一个模板到当前模板中
{% include 'header.html' %} Body {% include 'footer.html' %}import Jinja2支持将常用的代码放到宏中。这些宏可以放到不同的模板中,然后用import语句导入来使用,这有点类似python的import功能。需要注意的是,import导入的模板会被缓存,而且导入到模板不能访问当前模板的本地变量,它只能访问全局变量。 导入模板有两种方式,一是导入整个的模板作为一个变量,另一个方法是从一个模板中导入指定的宏或者可导出的变量 下面我们写一个名为"form.html"的模板, 这个模板作为一个公共模板提供给其它模板使用
{% macro input(name, value='', type='text') -%} {%- endmacro %} {%- macro textarea(name, value='', rows=10, cols=40) -%} {{ value|e }} {%- endmacro %}最简单和灵活的方式是把form.html整个导入到一个模板中
{% import 'forms.html' as forms %}
{{ forms.textarea('comment') }}
或者导入指定的内容(宏或者变量)到当前模板中{% from 'forms.html' import input as input_field, textarea %}
{{ textarea('comment') }}
表达式 Jinja的表达式在模板中到处都是,它的语法很类似python,而且它很简单,即使不会python也可以很容易学会它。 字面值 字面值是最简单的表达式,它其实就是一个python的对象,在Jinja中有下面几种字面值: 字符串,数字,序列,元组,字典,bool类型。 它们的用法很python的很接近,如下面的例子:{{ 1 in [1, 2, 3] }}'''is''' 执行一个检查器 '''|''' 执行一个过滤器 '''~''' 连接字符串 '{{ "Hello " ~ name ~ "!" }}',如果name的值是'world, 显示的内容将是 "Hello world" '''( )''' 调用函数 '''. / []''' 访问一个对象的属性 if表达式 Jinja支持内联表达式,在某些情况下非常有用,例如:
{% extends layout_template if layout_template is defined else 'master.html' %}这个例子的意思是:如果变量layout_template已定义则导入,否则导入master.html 通用的语法规则是''' if else ''' 内建过滤器 *'''abs(number)''' 返回数字的绝对值 *'''batch(value, linecount, fill_with=None)''' :将一个序列以给定值分成若干片,如果给定了fill_with,则会将fill_with补充到未分配的部分。比如一个序列 ['a','b','c','d','e'], 用数值3分片将会得到[['a','b','c'], ['d','e']], 如果分片时指定fill_with=' ',结果将会是[['a','b','c'], ['d','e',' ']] :这个过滤器的用处在于,如果你要在表格中显示一个很长的序列,每行显示5个,则可以用下面的方式打印:
{% for row in seq|batch(3, ' ') %} {% for item in row %}*'''capitalize(s)''' 首字符大写 *'''center(value, width=80)''' 生成一个长度为width的空字符串,将value放在中间 *'''default(value, default_value=u”, boolean=False)''' 如果value未定义,则显示default_value,如果value是一个bool型,需要将boolean置为true,这样当value为false是将会打印缺省值 这个过滤器的别名是d *'''dictsort(value, case_sensitive=False, by='key')''' 字典排序,case_sensitive决定是否大小写敏感,by决定是按照key排序还是按value排序 *'''escape(s)''' html字符转义,别名是e *'''filesizeformat(value)''' 将一个大数字转换成KMG形式,如:1.3k,34g,25.3m等等 *'''first(seq)''' 返回序列的第一个值 *'''float(value, default=0.0)''' 将一个值转换成浮点数,如果转换失败则返回default *'''forceescape(value)''' 不管value是否被转义过,一律进行html转义。比如value="<", 如果用“value|e|e”则会得到“<",而不是"<",如果用forceescape则会得到"<" *'''format(value, *args, **kwargs)''' 等同于python的"%s,%s" % (str1, str2) *'''groupby(value, attribute)''' 类似SQL的group by,可以将一个序列里的对象/字典,按照attribute分组。如下例:
...结果会是这个样子: