Jusene's Blog

Python模版渲染引擎Jinja2

字数统计: 7.4k阅读时长: 34 min
2017/12/06 Share

Jinja2

jinja模版只是一个文本文件,jinja可以将模版生成任何基于文本的格式,如html,jinja模版并不需要一个特定的扩展名:.html,.xml等都可以,模版中包含的变量或者表达式,当模版被渲染时,会被替换成值和标签,并且可以在模版中控制逻辑。

简单例子:

1
<!DOCTYPE html>
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, '&nbsp;') %}
    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
    @app.template_filter('sub')
    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 代码:
    1
    import re
    2
    def has_number(str):
    3
      return re.match(r'.*\d+',str)
    4
    app.add_template_test(has_number,'contain_number')
    我们定义一个has_number函数,用正则来判断输入参数是否包含数字,然后调用‘app.add_template_test’方法,第一个是测试器函数,第二个是测试器名称。
    1
    {% if name is contain_number %}
    2
      <h2>{{ name }} contains number.</h2>
    3
    {% endif %}
    同过滤器一样,Flask提供添加测试器的装饰器‘template_test’:
    1
    @app.template_test('end_with')
    2
    def end_with(str,suffix):
    3
      return str.lower().endwith(suffix.lower())
    1
    {% if name is end_with 'me' %}
    2
      <h2>{{ name }} ends with 'me'</h2>
    3
    {% endif %}
    Flask添加测试器的方法是封装了对jinja2环境变量的操作:
    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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
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
    &copy;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 节包裹起来:
THIS TEXT BECOMES UPPERCASE
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 %}
CATALOG
  1. 1. Jinja2
    1. 1.1. 变量
    2. 1.2. 过滤器
    3. 1.3. 测试
    4. 1.4. 注释
    5. 1.5. 空白控制
    6. 1.6. 转义
    7. 1.7. 行语句
    8. 1.8. 模版继承
      1. 1.8.1. 基本模版
      2. 1.8.2. 子模版
      3. 1.8.3. super块
      4. 1.8.4. 命名结束标签
      5. 1.8.5. 嵌套块和作用域
    9. 1.9. HTML转义
      1. 1.9.1. 手动转义
      2. 1.9.2. 自动转义
    10. 1.10. 控制模版结构
      1. 1.10.1. For
      2. 1.10.2. If
    11. 1.11.
    12. 1.12. 调用
      1. 1.12.1. 包含
      2. 1.12.2. 导入
      3. 1.12.3. 导入上下文行文
    13. 1.13. 表达式
      1. 1.13.1. 字面量
      2. 1.13.2. 算术
      3. 1.13.3. 比较
      4. 1.13.4. 逻辑
      5. 1.13.5. 其他运算符
      6. 1.13.6. if表达式
    14. 1.14. 全局函数清单
    15. 1.15. 扩展
      1. 1.15.1. with语句