Template filters that expect strings¶
django.template.defaultfilters.
stringfilter()
¶
如果你正在编写一个只希望用一个字符串来作为第一个参数的模板过滤器,你应当使用stringfilter
装饰器。 这将在对象被传入你的函数之前把这个对象转换成它的字符串值:
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.filter
@stringfilter
def lower(value):
return value.lower()
用这种方式,你甚至可以给这个过滤器传递一个整数,并且不会出现AttributeError
(因为整数没有 lower()
方法).
过滤器和自动转义¶
编写一个自定义的过滤器时,请考虑一下过滤器如何与Django 的自定转义行为相互作用。 请注意有三种类型的字符串可以传递给模板中的代码:
-
原始字符串 即Python 原生的
str
或unicode
类型。 输出时,如果自动转义生效则进行转义,否则保持不变。 -
安全字符串 是指在输出时已经被标记为安全而不用进一步转义的字符串。 任何必要的转义已经完成。 它们通常用于包含HTML 的输出,并希望在客户端解释为原始的形式。
在内部,这些字符串是
SafeBytes
或SafeText
类型。 它们共享一个公共基类SafeData
,所以你可以使用类似的代码测试他们︰if isinstance(value, SafeData): # Do something with the "safe" string. ...
模板过滤代码最终是这两种中的一个:
-
你的过滤器没有引进任何HTML 不安全字符(
<
、'
、>
、"
或&
)到结果中。 在这种情况下,你可以让Django 照顾你的所有的自动转义处理。 你需要做的就是当你注册过滤器函数的时候将is_safe
标志设置为True
,如下所示︰@register.filter(is_safe=True) def myfilter(value): return value
这个标志告诉Django 如果"安全"的字符串传递到您的过滤器,结果仍将是"安全",如果一个非安全字符串传递,如果必要Django 会自动转义它。
你可以认为这个的意思是"此过滤器是安全的 —— 它没有引入任何不安全的HTML 的可能性。
is_safe
存在的必要原因是因为有很多正常的字符串操作会将一个SafeData
对象转换回普通的str
或unicode
对象而不是试图捕获它们,Django 在过滤器完成之后会修复这种破坏。例如,假设你有一个过滤器将字符串
xx
添加到任何输入的末尾。 因为这没有引入危险的HTML 字符(已经存在的除外)的结果,你应该使用is_safe
标记你的过滤器:@register.filter(is_safe=True) def add_xx(value): return '%sxx' % value
当这个过滤器用在模板中启用自动转义的地方时,如果输入没有标记为“安全”,Django 将对输出进行转义。
默认情况下,
is_safe
为False
,你可以在不需要的任何过滤器中省略它。决定你的过滤器是否真的会保持安全字符串是安全的时要小心。 如果你在删除字符,可能会无意中在结果留下不平衡的 HTML 标记或实体。 例如,从输入删除
>
可能将<a
转变成<a>
,这将需要对输出进行转义,避免造成问题。 同样,删除一个分号(;
) 可以将&
转变成&
,不再是一个有效的实体,因此需要进一步的转义。 大多数情况下不会这么棘手,但在审查你的代码时要留意任何类似的问题。标记过滤器位
is_safe
将强制过滤器的返回值为字符串。 如果你的过滤器应返回一个布尔值或其他非字符串值,则将其标记is_safe
会有意想不到的后果 (如将布尔值 False 转换为字符串 'False')。 -
或者,你的过滤器代码手动照顾任何必要的转义。 这在你正引入新的HTML 标记到结果中时是必要的。 你想标记输出为安全的而不用进一步的转义,所以你需要自己处理输入输出。
用
django.utils.safestring.mark_safe()
标记输出为安全字符。但你要小心。 你需要做的不仅仅只是标记作为安全输出。 您需要确保它真的是安全的,而你做什么取决于自动转义是否有效。 这个想法的目的是编写的过滤器在无论模板自动转义是打开或关闭时都可以工作,这样模板作者使用起来更简单。
为了使你的过滤器知道当前的自动转义状态,当你注册过滤器函数时需要设置
needs_autoescape
标志为True
。 (如果不指定此标志,则默认为False
)。 此标志告诉Django 你的过滤器函数想要被传递一个额外的关键字参数,称为autoescape
,如果启用自动转义则为True
,否则为False
。 建议设置autoescape
参数的默认值设置为True
,这样如果从Python 代码中调用该函数则会自动启用转义。例如,让我们编写一个强调字符串第一个字符的过滤器︰
from django import template from django.utils.html import conditional_escape from django.utils.safestring import mark_safe register = template.Library() @register.filter(needs_autoescape=True) def initial_letter_filter(text, autoescape=True): first, other = text[0], text[1:] if autoescape: esc = conditional_escape else: esc = lambda x: x result = '<strong>%s</strong>%s' % (esc(first), esc(other)) return mark_safe(result)
needs_autoescape
标志和autoescape
关键字参数意味着我们的函数将知道当调用过滤器时自动转义是否有效。 我们使用autoescape
来决定是否需要通过django.utils.html.conditional_escape
传递输入数据。 (在后一种情况下,我们只使用identity函数作为“escape”函数。)conditional_escape()
函数与escape()
类似,只不过它只转义不是SafeData
实例的输入. 如果将SafeData
实例传递给conditional_escape()
,则数据不会改变。最后,在上面的例子中,我们记得将结果标记为安全的,这样我们的HTML直接插入到模板中,而不需要进一步转义。
在这种情况下,没有必要担心
is_safe
标志(虽然包括它不会伤害任何东西)。 每当你手动处理自动转义问题并返回一个安全的字符串,is_safe
标志不会改变任何方式。
警告
在重用内置过滤器时避免XSS漏洞
Django的内置过滤器默认情况下具有autoescape=True
,以便获得正确的自动转义行为并避免跨站点脚本漏洞。
在旧版本的Django中,在重新使用Django的内置过滤器时请小心,因为autoescape
默认为None
。 您需要传递autoescape=True
才能获得自动转义。
例如,如果您想编写一个称为urlize_and_linebreaks
的自定义过滤器,它结合了urlize
和linebreaksbr
过滤器,过滤器将如下所示:
from django.template.defaultfilters import linebreaksbr, urlize
@register.filter(needs_autoescape=True)
def urlize_and_linebreaks(text, autoescape=True):
return linebreaksbr(
urlize(text, autoescape=autoescape),
autoescape=autoescape
)
然后:
{{ comment|urlize_and_linebreaks }}
将等同于︰
{{ comment|urlize|linebreaksbr }}