Home » Uncategorized » Flask的Jinja2模板引擎 — 过滤器(3rd)

Flask的Jinja2模板引擎 — 过滤器(3rd)

我所了解的模板引擎大部分都会提供类似Jinja2过滤器的功能,只不过叫法不同罢了。比如PHP Smarty中的Modifiers(变量调节器或修饰器),FreeMarker中的Build-ins(内建函数),连AngularJS这样的前端框架也提供了Filter过滤器。它们都是用来在变量被显示或使用前,对其作转换处理的。可以把它认为是一种转换函数,输入的参数就是其所修饰的变量,返回的就是变量转换后的值。

过滤器使用
回到我们第一篇的例子,我们在模板中对变量name作如下处理:

Hello {{ name | upper }}!

1

Hello {{ name | upper }}!

你会看到name的输出都变成大写了。这就是过滤器,只需在待过滤的变量后面加上”|”符号,再加上过滤器名称,就可以对该变量作过滤转换。上面例子就是转换成全大写字母。过滤器可以连续使用:

Hello {{ name | upper | truncate(3, True) }}!

1

Hello {{ name | upper | truncate(3, True) }}!

现在name变量不但被转换为大写,而且当它的长度大于3后,只显示前3个字符,后面默认用”…”显示。过滤器”truncate”有3个参数,第一个是字符截取长度;第二个决定是否保留截取后的子串,默认是False,也就是当字符大于3后,只显示”…”,截取部分也不出现;第三个是省略符号,默认是”…”。

其实从例子中我们可以猜到,过滤器本质上就是一个转换函数,它的第一个参数就是待过滤的变量,在模板中使用时可以省略去。如果它有第二个参数,模板中就必须传进去。

内置过滤器 Builtin Filters
Jinja2模板引擎提供了丰富的内置过滤器。这里介绍几个常用的。

字符串操作

{# 当变量未定义时,显示默认字符串,可以缩写为d #}

{{ name | default(‘No name’, true) }}

{# 单词首字母大写 #}

{{ ‘hello’ | capitalize }}

{# 单词全小写 #}

{{ ‘XML’ | lower }}

{# 去除字符串前后的空白字符 #}

{{ ‘ hello ‘ | trim }}

{# 字符串反转,返回”olleh” #}

{{ ‘hello’ | reverse }}

{# 格式化输出,返回”Number is 2″ #}

{{ ‘%s is %d’ | format(“Number”, 2) }}

{# 关闭HTML自动转义 #}

{{ ‘name‘ | safe }}

{% autoescape false %}
{# HTML转义,即使autoescape关了也转义,可以缩写为e #}

{{ ‘name‘ | escape }}

{% endautoescape %}

{# 当变量未定义时,显示默认字符串,可以缩写为d #}

{{ name | default(‘No name’, true) }}

{# 单词首字母大写 #}

{{ ‘hello’ | capitalize }}

{# 单词全小写 #}

{{ ‘XML’ | lower }}

{# 去除字符串前后的空白字符 #}

{{ ‘ hello ‘ | trim }}

{# 字符串反转,返回”olleh” #}

{{ ‘hello’ | reverse }}

{# 格式化输出,返回”Number is 2″ #}

{{ ‘%s is %d’ | format(“Number”, 2) }}

{# 关闭HTML自动转义 #}

{{ ‘name‘ | safe }}

{% autoescape false %}
{# HTML转义,即使autoescape关了也转义,可以缩写为e #}

{{ ‘name‘ | escape }}

{% endautoescape %}
数值操作

{# 四舍五入取整,返回13.0 #}

{{ 12.8888 | round }}

{# 向下截取到小数点后2位,返回12.88 #}

{{ 12.8888 | round(2, ‘floor’) }}

{# 绝对值,返回12 #}

{{ -12 | abs }}

1
2
3
4
5
6
7
8
{# 四舍五入取整,返回13.0 #}

{{ 12.8888 | round }}

{# 向下截取到小数点后2位,返回12.88 #}

{{ 12.8888 | round(2, ‘floor’) }}

{# 绝对值,返回12 #}

{{ -12 | abs }}

列表操作

{# 取第一个元素 #}

{{ [1,2,3,4,5] | first }}

{# 取最后一个元素 #}

{{ [1,2,3,4,5] | last }}

{# 返回列表长度,可以写为count #}

{{ [1,2,3,4,5] | length }}

{# 列表求和 #}

{{ [1,2,3,4,5] | sum }}

{# 列表排序,默认为升序 #}

{{ [3,2,1,5,4] | sort }}

{# 合并为字符串,返回”1 | 2 | 3 | 4 | 5″ #}

{{ [1,2,3,4,5] | join(‘ | ‘) }}

{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}

{{ [‘tom’,’bob’,’ada’] | upper }}

{# 取第一个元素 #}

{{ [1,2,3,4,5] | first }}

{# 取最后一个元素 #}

{{ [1,2,3,4,5] | last }}

{# 返回列表长度,可以写为count #}

{{ [1,2,3,4,5] | length }}

{# 列表求和 #}

{{ [1,2,3,4,5] | sum }}

{# 列表排序,默认为升序 #}

{{ [3,2,1,5,4] | sort }}

{# 合并为字符串,返回”1 | 2 | 3 | 4 | 5″ #}

{{ [1,2,3,4,5] | join(‘ | ‘) }}

{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}

{{ [‘tom’,’bob’,’ada’] | upper }}

字典列表操作

{% set users=[{‘name’:’Tom’,’gender’:’M’,’age’:20},
{‘name’:’John’,’gender’:’M’,’age’:18},
{‘name’:’Mary’,’gender’:’F’,’age’:24},
{‘name’:’Bob’,’gender’:’M’,’age’:31},
{‘name’:’Lisa’,’gender’:’F’,’age’:19}]
%}

{# 按指定字段排序,这里设reverse为true使其按降序排 #}

    {% for user in users | sort(attribute=’age’, reverse=true) %}

  • {{ user.name }}, {{ user.age }}
  • {% endfor %}

{# 列表分组,每组是一个子列表,组名就是分组项的值 #}

    {% for group in users|groupby(‘gender’) %}

  • {{ group.grouper }}
      {% for user in group.list %}

    • {{ user.name }}
    • {% endfor %}

  • {% endfor %}

{# 取字典中的某一项组成列表,再将其连接起来 #}

{{ users | map(attribute=’name’) | join(‘, ‘) }}

{% set users=[{‘name’:’Tom’,’gender’:’M’,’age’:20},
{‘name’:’John’,’gender’:’M’,’age’:18},
{‘name’:’Mary’,’gender’:’F’,’age’:24},
{‘name’:’Bob’,’gender’:’M’,’age’:31},
{‘name’:’Lisa’,’gender’:’F’,’age’:19}]
%}

{# 按指定字段排序,这里设reverse为true使其按降序排 #}

    {% for user in users | sort(attribute=’age’, reverse=true) %}

  • {{ user.name }}, {{ user.age }}
  • {% endfor %}

{# 列表分组,每组是一个子列表,组名就是分组项的值 #}

    {% for group in users|groupby(‘gender’) %}

  • {{ group.grouper }}
      {% for user in group.list %}

    • {{ user.name }}
    • {% endfor %}

  • {% endfor %}

{# 取字典中的某一项组成列表,再将其连接起来 #}

{{ users | map(attribute=’name’) | join(‘, ‘) }}

更全的内置过滤器介绍可以从Jinja2的官方文档中找到。

Flask内置过滤器
Flask提供了一个内置过滤器”tojson”,它的作用是将变量输出为JSON字符串。这个在配合Javascript使用时非常有用。我们延用上节字典列表操作中定义的”users”变量


1
2
3
4

注意,这里要避免HTML自动转义,所以加上safe过滤器。

语句块过滤
Jinja2还可以对整块的语句使用过滤器。

{% filter upper %}
This is a Flask Jinja2 introduction.
{% endfilter %}
1
2
3
{% filter upper %}
This is a Flask Jinja2 introduction.
{% endfilter %}
不过上述这种场景不经常用到。

自定义过滤器
内置的过滤器不满足需求怎么办?自己写呗。过滤器说白了就是一个函数嘛,我们马上就来写一个。回到Flask应用代码中:

def double_step_filter(l):
return l[::2]
1
2
def double_step_filter(l):
return l[::2]
我们定义了一个”double_step_filter”函数,返回输入列表的偶数位元素(第0位,第2位,..)。怎么把它加到模板中当过滤器用呢?Flask应用对象提供了”add_template_filter”方法来帮我们实现。我们加入下面的代码:

app.add_template_filter(double_step_filter, ‘double_step’)
1
app.add_template_filter(double_step_filter, ‘double_step’)
函数的第一个参数是过滤器函数,第二个参数是过滤器名称。然后,我们就可以愉快地在模板中使用这个叫”double_step”的过滤器了:

{# 返回[1,3,5] #}

{{ [1,2,3,4,5] | double_step }}

1
2
{# 返回[1,3,5] #}

{{ [1,2,3,4,5] | double_step }}

Flask还提供了添加过滤器的装饰器”template_filter”,使用起来更简单。下面的代码就添加了一个取子列表的过滤器。装饰器的参数定义了该过滤器的名称”sub”。

@app.template_filter(‘sub’)
def sub(l, start, end):
return l[start:end]
1
2
3
@app.template_filter(‘sub’)
def sub(l, start, end):
return l[start:end]
我们在模板中可以这样使用它:

{# 返回[2,3,4] #}

{{ [1,2,3,4,5] | sub(1,4) }}

1
2
{# 返回[2,3,4] #}

{{ [1,2,3,4,5] | sub(1,4) }}

Flask添加过滤器的方法实际上是封装了对Jinja2环境变量的操作。上述添加”sub”过滤器的方法,等同于下面的代码。

app.jinja_env.filters[‘sub’] = sub
1
app.jinja_env.filters[‘sub’] = sub
我们在Flask应用中,不建议直接访问Jinja2的环境变量。如果离开Flask环境直接使用Jinja2的话,就可以通过”jinja2.Environment”来获取环境变量,并添加过滤器。

完整代码如下:

from flask import Flask,render_template

app = Flask(__name__)

@app.route(‘/hello’)
@app.route(‘/hello/‘)
def hello(name=None):
return render_template(‘hello-3.html’, name=name)

########## Add Filter ##########
def double_step_filter(l):
return l[::2]
app.add_template_filter(double_step_filter, ‘double_step’)

@app.template_filter(‘sub’)
def sub(l, start, end):
return l[start:end]

#app.jinja_env.filters[‘sub’] = sub

if __name__ == ‘__main__’:
app.run(host=’0.0.0.0′, debug=True)

from flask import Flask,render_template

app = Flask(__name__)

@app.route(‘/hello’)
@app.route(‘/hello/‘)
def hello(name=None):
return render_template(‘hello-3.html’, name=name)

########## Add Filter ##########
def double_step_filter(l):
return l[::2]
app.add_template_filter(double_step_filter, ‘double_step’)

@app.template_filter(‘sub’)
def sub(l, start, end):
return l[start:end]

#app.jinja_env.filters[‘sub’] = sub

if __name__ == ‘__main__’:
app.run(host=’0.0.0.0′, debug=True)


Hello Sample

{% if name %}

Hello {{ name | upper | truncate(3, True) }}!

{% else %}

Hello World!

{% endif %}

{# 字符串操作 #}
{# 当变量未定义时,显示默认字符串 #}

{{ name | default(‘No name’, true) }}

{# 单词首字母大写 #}

{{ ‘hello’ | capitalize }}

{# 单词全小写 #}

{{ ‘XML’ | lower }}

{# 去除字符串前后的空白字符 #}

{{ ‘ hello ‘ | trim }}

{# 字符串反转,返回”olleh” #}

{{ ‘hello’ | reverse }}

{# 格式化输出,返回”Number is 2″ #}

{{ ‘%s is %d’ | format(“Number”, 2) }}

{# 关闭HTML自动转义 #}

{{ ‘name‘ | safe }}

{% autoescape false %}
{# HTML转义,即使autoescape关了也转义 #}

{{ ‘name‘ | escape }}

{% endautoescape %}

{# 数值操作 #}
{# 四舍五入取整,返回13.0 #}

{{ 12.8888 | round }}

{# 向下截取到小数点后2位,返回12.88 #}

{{ 12.8888 | round(2, ‘floor’) }}

{# 绝对值,返回12 #}

{{ -12 | abs }}

{# 列表操作 #}
{# 取第一个元素 #}

{{ [1,2,3,4,5] | first }}

{# 取最后一个元素 #}

{{ [1,2,3,4,5] | last }}

{# 返回列表长度 #}

{{ [1,2,3,4,5] | length }}

{# 列表求和 #}

{{ [1,2,3,4,5] | sum }}

{# 列表排序,默认为升序 #}

{{ [3,2,1,5,4] | sort }}

{# 合并为字符串,返回”1 | 2 | 3 | 4 | 5″ #}

{{ [1,2,3,4,5] | join(‘ | ‘) }}

{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}

{{ [‘tom’,’bob’,’ada’] | upper }}

{# 字典列表操作 #}
{% set users=[{‘name’:’Tom’,’gender’:’M’,’age’:20},
{‘name’:’John’,’gender’:’M’,’age’:18},
{‘name’:’Mary’,’gender’:’F’,’age’:24},
{‘name’:’Bob’,’gender’:’M’,’age’:31},
{‘name’:’Lisa’,’gender’:’F’,’age’:19}]
%}

{# 按指定字段排序,这里设reverse为true使其按降序排 #}

    {% for user in users | sort(attribute=’age’, reverse=true) %}

  • {{ user.name }}, {{ user.age }}
  • {% endfor %}

{# 列表分组,每组是一个子列表,组名就是分组项的值 #}

    {% for group in users|groupby(‘gender’) %}

  • {{ group.grouper }}
      {% for user in group.list %}

    • {{ user.name }}
    • {% endfor %}

  • {% endfor %}

{# 取字典中的某一项组成列表,再将其连接起来 #}

{{ users | map(attribute=’name’) | join(‘, ‘) }}

{# tojson #}

{% filter upper %}
This is a Flask Jinja2 introduction.
{% endfilter %}

{# 自定义过滤器 #}
{# 返回[1,3,5] #}

{{ [1,2,3,4,5] | double_step }}

{# 返回[2,3,4] #}

{{ [1,2,3,4,5] | sub(1,4) }}


Hello Sample

{% if name %}

Hello {{ name | upper | truncate(3, True) }}!

{% else %}

Hello World!

{% endif %}

{# 字符串操作 #}
{# 当变量未定义时,显示默认字符串 #}

{{ name | default(‘No name’, true) }}

{# 单词首字母大写 #}

{{ ‘hello’ | capitalize }}

{# 单词全小写 #}

{{ ‘XML’ | lower }}

{# 去除字符串前后的空白字符 #}

{{ ‘ hello ‘ | trim }}

{# 字符串反转,返回”olleh” #}

{{ ‘hello’ | reverse }}

{# 格式化输出,返回”Number is 2″ #}

{{ ‘%s is %d’ | format(“Number”, 2) }}

{# 关闭HTML自动转义 #}

{{ ‘name‘ | safe }}

{% autoescape false %}
{# HTML转义,即使autoescape关了也转义 #}

{{ ‘name‘ | escape }}

{% endautoescape %}

{# 数值操作 #}
{# 四舍五入取整,返回13.0 #}

{{ 12.8888 | round }}

{# 向下截取到小数点后2位,返回12.88 #}

{{ 12.8888 | round(2, ‘floor’) }}

{# 绝对值,返回12 #}

{{ -12 | abs }}

{# 列表操作 #}
{# 取第一个元素 #}

{{ [1,2,3,4,5] | first }}

{# 取最后一个元素 #}

{{ [1,2,3,4,5] | last }}

{# 返回列表长度 #}

{{ [1,2,3,4,5] | length }}

{# 列表求和 #}

{{ [1,2,3,4,5] | sum }}

{# 列表排序,默认为升序 #}

{{ [3,2,1,5,4] | sort }}

{# 合并为字符串,返回”1 | 2 | 3 | 4 | 5″ #}

{{ [1,2,3,4,5] | join(‘ | ‘) }}

{# 列表中所有元素都全大写。这里可以用upper,lower,但capitalize无效 #}

{{ [‘tom’,’bob’,’ada’] | upper }}

{# 字典列表操作 #}
{% set users=[{‘name’:’Tom’,’gender’:’M’,’age’:20},
{‘name’:’John’,’gender’:’M’,’age’:18},
{‘name’:’Mary’,’gender’:’F’,’age’:24},
{‘name’:’Bob’,’gender’:’M’,’age’:31},
{‘name’:’Lisa’,’gender’:’F’,’age’:19}]
%}

{# 按指定字段排序,这里设reverse为true使其按降序排 #}

    {% for user in users | sort(attribute=’age’, reverse=true) %}

  • {{ user.name }}, {{ user.age }}
  • {% endfor %}

{# 列表分组,每组是一个子列表,组名就是分组项的值 #}

    {% for group in users|groupby(‘gender’) %}

  • {{ group.grouper }}
      {% for user in group.list %}

    • {{ user.name }}
    • {% endfor %}

  • {% endfor %}

{# 取字典中的某一项组成列表,再将其连接起来 #}

{{ users | map(attribute=’name’) | join(‘, ‘) }}

{# tojson #}

{% filter upper %}
This is a Flask Jinja2 introduction.
{% endfilter %}

{# 自定义过滤器 #}
{# 返回[1,3,5] #}

{{ [1,2,3,4,5] | double_step }}

{# 返回[2,3,4] #}

{{ [1,2,3,4,5] | sub(1,4) }}