bigywater.tech
Django · 85

自定义 Template Filter

自定义过滤器几行就能写完,但转义、异常、调用次数这三件事常被忽略。这篇写七个小过滤器,逐个验证它们在模板里的真实行为。

1 个参数
过滤器最多接受一个参数;两个会 TemplateSyntaxError
is_safe / needs_autoescape
决定过滤器的返回值是否被再转义
不缓存
同一个过滤器表达式每写一次、每次循环迭代都调用一次

基本用法

实测 · 本机执行输出
{{ v|shout }}          → HI!
{{ v|add_str:"-x" }}   → hi-x
链式 {{ v|shout|add_str:"?" }} → HI!?
变量作为参数 {{ v|add_str:suffix }} → hi_s
两个参数 → TemplateSyntaxError: Could not parse the remainder: ':"b"' from 'v|add_str:"a":"b"'

过滤器就是普通函数,第一个参数是变量值,第二个(可选)是过滤器参数。

转义

实测 · 本机执行输出
返回字符串的普通过滤器: <I>!
is_safe=True,输入是 safe: <i><end>
is_safe=True,输入不是 safe: &lt;i&gt;&lt;end&gt;
needs_autoescape,自动转义开: <b>&lt;i&gt;</b>
needs_autoescape,{% autoescape off %} 里: <b><i></b>

返回 HTML 的过滤器要么用 needs_autoescape 自己转义输入再 mark_safe,要么用 format_html;is_safe=True 只是「如果输入是 safe,输出仍是 safe」,不会让不安全的输入变安全。

错误

实测 · 本机执行输出
boom 过滤器 → ValueError: filter exploded
变量不存在:{{ nope|shout }} → '!'  ← 过滤器收到的是空字符串 "",于是得到 "!"
没有对应过滤器: TemplateSyntaxError: Invalid filter: 'nofilter'
对 None 用 len 的过滤器 → TypeError: object of type 'NoneType' has no len()

模板里访问不存在的变量不报错(得到 string_if_invalid),但过滤器里的异常一定会传出来。

调用次数

实测 · 本机执行输出
模板里写两次: 2
5 次循环: 5
{% with %} 先存下来再用两次: 1

过滤器里做数据库查询或重计算时要注意调用次数。

加载

实测 · 本机执行输出
没有 {% load myf %}: TemplateSyntaxError: Invalid filter: 'shout'

templatetags 目录需要有 __init__.py,库名就是文件名。

分步演示:自定义过滤器

自定义 Filter
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。用 engines["django"].from_string 渲染
    • 没有验证:expects_localtime、stringfilter 装饰器、过滤器抛 ValidationError 之外的错误页表现、在 Jinja2 后端里的写法
    京ICP备2026067502号-1京公网安备11010502063487号