如何在Django模板中为自定义模板过滤器添加多个参数?

115
这是我的自定义过滤器:
from django import template

register = template.Library()

@register.filter
def replace(value, cherche, remplacement):
    return value.replace(cherche, remplacement)

这是我在模板文件中尝试使用它时出现错误的方式:
{{ attr.name|replace:"_"," " }}
{{ attr.name|replace:"_" " " }}
{{ attr.name|replace:"_":" " }}
{{ attr.name|replace:"cherche='_', remplacement=' '" }}

我查看了Django的文档书籍,但只找到了使用单个参数的示例...这真的可能吗?

10个回答

120

这是可能的,而且相当简单。

Django只允许将一个参数传递给过滤器,但您可以使用逗号将所有参数放入单个字符串中。

因此,例如,如果您想要一个过滤器来检查变量X是否在列表[1,2,3,4]中,则需要一个模板过滤器,其代码如下:

{% if X|is_in:"1,2,3,4" %}

现在我们可以像这样创建您的模板标签:

from django.template import Library

register = Library()

def is_in(var, args):
    if args is None:
        return False
    arg_list = [arg.strip() for arg in args.split(',')]
    return var in arg_list

register.filter(is_in)

创建arg_list的代码行是一个生成器表达式,它将args字符串按逗号分隔,并调用.strip()方法删除任何前导和尾随空格。

例如,如果第三个参数是int类型,只需执行以下操作:

arg_list[2] = int(arg_list[2])

如果它们全部都是整数,可以这样做:

arg_list = [int(arg) for arg in args.split(',')]

编辑:现在来具体回答你的问题,使用键值对作为参数,你可以使用Django使用的同一类来解析URL中的查询字符串,这也有一个好处,就是根据您的settings.py正确地处理字符编码。

因此,与查询字符串一样,每个参数都由'&'分隔:

{{ attr.name|replace:"cherche=_&remplacement= " }}

那么现在你的替换函数将会变成这样:

from django import template
from django.http import QueryDict

register = template.Library()

@register.filter
def replace(value, args):
    qs = QueryDict(args)
    if qs.has_key('cherche') and qs.has_key('remplacement'):
        return value.replace(qs['cherche'], qs['remplacement'])
    else:
        return value

你可以冒一些错误替换的风险来加快这个过程:

qs = QueryDict(args)
return value.replace(qs.get('cherche',''), qs.get('remplacement',''))

1
如果这些值是存储在变量中的,如何实现呢? - Anto
2
这似乎很有帮助,但我无法让它与传递的变量一起工作。为了做到这一点,我必须使用tagsimple_tag - 它允许传递多个变量,甚至是命名变量。 - Furbeenator
2
我来自未来,但在我看来,使用标签似乎比在过滤器中实质上编写解析器更好。 - logicOnAbstractions

35

比你想象的更简单

你可以使用simple_tag完成此操作。

from django import template
register = template.Library()

@register.simple_tag
def multiple_args_tag(a, b, c, d):
   #do your stuff
   return 

在模板中:

{% multiple_args_tag 'arg1' 'arg2' 'arg3' 'arg4' %}

注意:不要忘记重新启动服务器。


3
谢谢!当需要使用 safe 时,您可以按照以下方式使用它:{% multiple_args_tag 'arg1' 'arg2' 'arg3' 'arg4' as my_var_name %} 然后跟着 {{ my_var_name|safe }} - sitWolf

31

就这么简单。

@register.filter(name='one_more')
def one_more(_1, _2):
    return _1, _2

def your_filter(_1_2, _3)
    _1, _2 = _1_2
    print "now you have three arguments, enjoy"

{{ _1|one_more:_2|your_filter:_3 }}

非常感谢您提供这个解决方案。我稍微升级了一下,现在您可以链接不同长度的参数。https://gist.github.com/BrnoPCmaniak/e9552294b3059461f940a47143f58811 - Filip Dobrovolný
4
这应该是正确的答案!这是一个漂亮的Python解决方案(也许不是最好的Django解决方案,请参见@dragonroot的答案)。 - Antoine Draune
2
这是一个非常好的解决方案。感谢您的帮助!! :) - ashim888
可以正常工作,谢谢。@register.filter() 不需要添加名称,而且你的过滤器也需要 register.filter()。 - user3486626

21

不要使用过滤器,将您的标签注册为简单标签。 它们可以采用多个参数。 调用它的语法会有点不同,但这只是一些词法糖的变化。


2
这是我的问题的正确答案。为了将模板变量传递到此函数中,我必须使用 simple_tag - Furbeenator
1
这是一个很好的解决方案。绝对值得查看Django文档中有关简单标签的内容:https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#simple-tags - Gunther
这是我最能理解的。标签比过滤器更强大和复杂,因此,如果您的问题更加复杂,似乎比试图把方形钉子塞进圆孔更好的方法。 - logicOnAbstractions

17

根据文档中的这一节,不可能实现:

自定义过滤器仅是接受一个或两个参数的Python函数:

  • 变量(输入)的值 - 不一定是字符串。
  • 参数的值 - 可以有默认值,也可以完全省略。

如果您正在使用硬编码字符串,Van Gale的方法将起作用。Django票证[https://code.djangoproject.com/ticket/1199]支持自定义过滤器中的多个参数,并且已经接受了补丁。 - Jeff Bauer

3

那个问题票在去年(2013年)被标记为WONTFIX,他们的开发人员建议如果需要多个参数,可以使用自定义标签。 - Paul Lo

3

<my-site>/globaltags/replace.py

from django.template import Library

import re

register = Library()

def search(value, search):
    return re.sub(search, '#f4x@SgXXmS', value)

def replace(value, replace):
    return re.sub('#f4x@SgXXmS', replace, value)

register.filter(search)
register.filter(replace)

在模板中:
{{ "saniel"|search:"s"|replace:"d" }}

能否解释一下 #f4x@SgXXmS 这一部分? - Dan Abramov
1
只是一个用作占位符的随机字符串。我选择了这个字符串,因为我相信它不会成为输入字符串的一部分。例如,如果我使用“{}”而不是“#f4x@SgXXmS”,那么{{“使用{}代替[]”|search:“off”|replace:“of”}}将返回:“使用[]代替of”,而不是预期的结果:“使用{}代替[]”。 - theosp
6
哦,这很有道理。不过最好将其声明为“永不出现的子字符串”。 - Dan Abramov

0
例如,您可以使用分隔符,将多个值传递给replace过滤器,然后使用split()进行拆分以使用这些值。 *您可以并且应该使用其他不包含值的分隔符。
from django import template

register = template.Library()

@register.filter
def replace(value, args):
    cherche, remplacement = args.split(',')
    return value.replace(cherche, remplacement)

{{ "2023-06-23"|replace:"-,/" }} {% 2023/06/23 %}
                  {% Here ↑ %}

-1
你可以简单地这样做:
{% assign find_total_issued = dailysalesreport | find: "TotalIssued":"13" %}

public static List<object> Find(object collection, string column, string value)

当函数的抽象化为sjare时,它将到达目的地。


-2
这是一个不好的想法,但可以运行:
{{ xml|input_by_xpath:"{'type':'radio','xpath':'//result/value'}" }}

并且

@register.filter
def input_by_xpath(device, args): 
    args = eval(args)
    ...
    result = "<input type=\"%s\" value=\"%s\" name=\"%s\"/>"%(args['type'],value,args['xpath'])
    return mark_safe(result)

小提示:eval可能会导致安全漏洞。您应该使用json.loads代替。 - Samuel Dauzon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接