过滤器和测试器
在 Python 中,如果需要对某个变量进行处理,可以通过函数来实现,而在模板中,则是通过过滤器来实现的。过滤器本质上也是函数,但是在模板中使用的方式是通过管道符号(|
)来调用的。例如,有一个字符串类型的变量 name,要获取它的长度,可以通过 {{ name|length }}
来获取,Jinja2 会把 name 当作第 1 个参数传给 length 过滤器底层对应的函数。length 是 Jinja2 内置好的过滤器,Jinja2 中内置了许多好用的过滤器,如果内置的过滤器不能满足需求,还可以自定义过滤器。下面先来学习如何自定义过滤器,读者明白了过滤器的原理后,再去学习 Jinja2 内置的过滤器就会更加得心应手了。
自定义过滤器
过滤器本质上是 Python 的函数,它会把被过滤的值当作第 1 个参数传给这个函数,函数经过一些逻辑处理后,再返回新的值。过滤器函数写好后可以通过 @app.template_filter 装饰器或者 app.add_template_filter 函数把函数注册成 Jinja2 能用的过滤器。这里以注册一个时间格式化的过滤器为例,来说明自定义过滤器的方法,示例代码如下。
def datetime_format(value, format="%Y-%d-%m %H:%M"):
return value.strftime(format)
app.add_template_filter(datetime_format, "dformat")
在上面代码中定义了一个 datetime_format 函数,第 1 个参数是需要被处理的值,第 2 个参数是时间的格式,并且指定了一个默认值。下面通过 app.add_template_filter 将 datetime_format 函数注册成了过滤器,过滤器的名字叫作 dformat。那么以后在模板文件中,就可以按如下方式使用了。
{{ article.pub_date|dformat }}
{{ article.pub_date|dformat("%B %Y") }}
如果 app.add_template_filter 没有传第 2 个参数,那么默认将使用函数的名称作为过滤器的名称。如以上注册过滤器代码可以改成以下代码。
...
app.add_template_filter(datetime_format)
在模板中则按以下方式使用。
{{ article.pub_date|datetime_format }}
...
当然,也可以通过 @app.template_filter
装饰器在函数定义时,就将它注册成过滤器。如以上的 datetime_format
函数,可以改写为如下形式。
@app.template_filter("dformat")
def datetime_format(value, format="%Y-%d-%m %H:%M"):
return value.strftime(format)
datetime_format 被 @app.template_filter 装饰后,就会自动被注册进 Jinja2 的过滤器中,并且 @app.template_filter 中的参数即为自定义过滤器的名称,如果不传参数,也会自动使用函数名称作为过滤器的名称。
Jinja2内置过滤器
在理解了 Jinja2 过滤器的原理后,再来学习 Jinja2 中内置过滤器,读者无须全部记住这些过滤器,只需在使用的时候翻阅本书或者阅读 Jinja2 官方文档 https://jinja.palletsprojects.com/en/3.0.x/templates/#builtin-filters 即可,用的次数多了自然会记住。
Jinja2 中内置过滤器如下。
-
abs(value):获取 value 的绝对值。
-
default(value,default_value,boolean=False):如果 value 没有定义,则返回第 2 个参数 default_value。如果要让 value 在被判断为 False 的情况下使用 default_value,则应该将后面的 boolean 参数设置为 False。先看以下示例。
<div>default过滤器:{{ user|default('admin') }}</div>
如果 user 没有定义,那么将会使用 admin 作为默认的值。再看以下示例。
<div>default过滤器:{{ ""|default('admin',boolean=True) }}</div>
因为 ""(空字符串)在使用 if 判断时,返回的是 False,这时如果要使用默认值 admin,就必须加上 boolean=True 参数。
-
escape(value):将一些特殊字符,如 &、<、>、"、' 进行转义。因为 Jinja2 默认开启了全局转义,所以在大部分情况下无须手动使用这个过滤器去转义,只有在关闭了转义的情况下,会需要使用到它。
-
filesizeformat(value,binary=False):将值格式化成方便阅读的单位。如 13KB、4.1MB、102Bytes 等。默认是 Mega、Giga,也就是每个相邻单位换算是 1000 倍。如果第 2 个参数设置为 True,那么相邻单位换算是 1024 倍。
-
first(value):返回 value 序列的第 1 个元素。
-
float(value,default=0.0):将 value 转换为浮点类型,如果转换失败会返回 0.0。
-
format(value,args,*kwargs):格式化字符串,示例代码如下。
{{ "%s,%s"|format(greeting,name) }}
-
groupby(value,attribute,default=None):value 是一个序列,可以使用参数 attribute 进行分组。例如,有一个 users 列表,里面的 user 都有一个 city 属性,如果要按照 city 进行分组,则可以使用以下代码实现。
<ul> {% for group in users|groupby("city") %} <li>{{ group.grouper }}: {{ group.list|join(", ") }}</li> {% endfor %} </ul>
-
int(value,default=0,base=10):转换为整型,如果转换失败会返回 0,并且默认按照十进制转换。
-
join(value,attribute):使用 attribute 指定的元素,将一个序列拼接成一个字符串。与 Python 中的 join 方法类似。
-
last(value):返回 value 序列的最后一个元素。
-
length(value):返回 value 序列的长度。
-
list(value):转换 value 为一个列表。
-
lower(value):将 value 全部转换为小写。
如要将 titles 序列中每个元素的值都变成小写形式,那么可以使用以下代码实现。
{{ titles|map('lower')}}
-
map(value,*args,**kwargs)
:将value这个序列都执行某个操作。如获取users这个序列中每个user的username字段。可以通过以下代码实现。
{{ users|map(attribute='username') }}
-
max(value):求序列中的最大值。
-
min(value):求序列中的最小值。
-
random(value):返回value这个序列中的一个随机值。
-
reject(value,*args,**kwargs)
:过滤value这个序列中的一些元素,过滤的条件通过后面的参数给定。如要过滤列表中所有的奇数,可以把Jinja2中内置的odd过滤器传给reject过滤器来实现,代码如下。{{ numbers|reject ('odd') }}
-
rejectattr(value,*args,**kwargs)
:根据 value 序列中元素的某个属性进行过滤。只要这个属性满足条件,那么就会被过滤掉,示例代码如下。{{ users|rejectattr("is_active") }} {{ users|rejectattr("email", "none") }}
上面第 1 行代码是过滤 users 中 is_active 为 True 的对象,第 2 行代码是过滤 users 中 email 为 none 的对象。
-
replace(value,old,new,count):将字符串 value 中的 old 替换为 new,并且可以通过 count 来确定替换多少个。与 Python 中字符串的 replace 方法用法一致。
-
reverse(value):将 value 这个序列逆序。
-
safe(value):在渲染 value 时,关闭自动转义。如以下代码所示。
<div>safe过滤器:{{ "<p style='background-color: red;'>中国</p>"|safe }}</div>
因为加了 safe 过滤器,就不会对前面的字符串进行转义,前面的字符串就会被当成 HTML 代码嵌入网页,从而看到一个红色的背景,背景中显示 “中国” 两个文字。
-
select(value,*args,**kwargs)
:选择 value 序列中满足条件的元素,与 reject 正好相反。 -
selectattr(value,*args,**kwargs)
:根据 value 序列中元素的某个属性进行过滤,留下满足条件的,过滤掉不满足条件的,与 rejectattr 正好相反。 -
sort(value,reverse=False,case_sensitive=False,attribute=None):将value这个序列进行排序,底层用的是Python的sorted函数,reverse代表是否逆向排序,case_sensitive代表是否忽略大小写,attribute代表根据value序列中元素的某个属性排序。
-
string(value):将value转换为字符串类型。
-
striptags(value):将字符串value中的HTML标签去除,留下文本内容。
-
tojson(value):将value转换为JSON格式的字符串。
-
trim(value):删除value前面和后面的空白字符(空格)。
-
truncate(value,length=255,killwords=False,end="…"):将字符串value进行截取,length代表保留多少个字符,killwords代表在截取字符串时是否要裁剪单词,end代表末尾的结束字符。这在文章简介、个人简介等只需要显示一部分字符的场景下非常有用。
-
unique(value,case_sensitive=False,attribute=None):将value序列中的重复元素删除。case_sensitive代表是否忽略大小写,attribute代表使用value序列中元素的某个属性。
-
upper(value):将value所有字符全部转换为大写。
-
urlencode(value):如果value是字符串,那么底层会调用Python的urllib.parse.quote;如果value是字典,那么底层会调用Python的urllib.parse.urlencode。
-
urlize(value,trim_url_limit=None,nofollow=False,target=None,rel=None,extra_schemes=None):将 value 变成可以单击的链接,如 URL 和邮箱。注意:value 必须是以 http://、https://、www.、mailto 开头的字符串。
-
wordcount(value):统计value中共有多少个单词。
-
xmlattr(value,autospace=True):value为一个字典,根据这个字典创建一个xml格式的属性,示例代码如下。
<ul {{ {'class': 'my_list', 'missing': none, 'id': 'list-%d'|format(variable)} | xmlattr }}>
...
</ul>
过滤器可以嵌套使用,如以下代码所示。
{{ titles|map("lower")|join(",") }}
在解析模板时,会先将 titles 传给 map 过滤器处理,得到结果后再传给 join 过滤器。
测试器
测试器用来测试某些元素是否满足某个条件,如测试一个变量是否是字符串、测试一个变量能否被调用等。以下代码通过演示 defined 测试器,来讲解测试器的使用。
{% if user is defined %}
user定义了 : {{ user }}
{% else %}
<!-- 这里可以添加没有定义user时的处理内容 -->
{% endif %}
可以看到,测试器是通过 if…is… 来使用的,if 后面是被测试的对象,is 后面是测试器。除了 defined 测试器,Jinja2 还提供了如表 4-1 所示的测试器。

表4-1 所示的测试器是 Jinja2 模板中内置的所有测试器,与过滤器的学习方式一样,读者可先简单阅读,无须强记,在需要使用时再翻阅 Jinja2 内置测试器的官方文档 https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-tests 即可。