Python lambda作为jinja2筛选器的参数?

Python lambda作为jinja2筛选器的参数?,python,lambda,jinja2,Python,Lambda,Jinja2,我希望在jinja2中有这样一个自定义过滤器: {{ my_list|my_real_map_filter(lambda i: i.something.else)|some_other_filter }} 但是当我实现它时,我得到了这个错误: TemplateSyntaxError: expected token ',', got 'i' jinja2的语法似乎不允许lambdas作为参数?有什么好办法吗?目前,我正在用python创建lambda,然后将其作为变量传递给模板,但我更希望能够

我希望在jinja2中有这样一个自定义过滤器:

{{ my_list|my_real_map_filter(lambda i: i.something.else)|some_other_filter }}
但是当我实现它时,我得到了这个错误:

TemplateSyntaxError: expected token ',', got 'i'

jinja2的语法似乎不允许lambdas作为参数?有什么好办法吗?目前,我正在用python创建lambda,然后将其作为变量传递给模板,但我更希望能够在模板中创建它。

不,您不能在Jinja2模板中将常规python表达式传递给过滤器

这种混淆源于jinja2模板在许多方面与Python语法相似,但您应该将其视为具有完全独立语法的代码

Jinja2有严格的规则,在模板的哪一部分可以预期,它通常不允许python代码保持原样,它期望表达式的确切类型,这是非常有限的


这与表示和模型应该分开的概念是一致的,所以模板不应该允许太多的逻辑。无论如何,与许多其他模板选项相比,Jinja2是非常允许的,并且在模板中允许相当多的逻辑。

我有一个解决方法,我正在对dict对象进行排序:

registers = dict(
    CMD = dict(
        address = 0x00020,
        name = 'command register'),
    SR = dict(
        address = 0x00010,
        name = 'status register'),
)
我想在寄存器目录上循环,但按地址排序。所以我需要一种按“地址”字段排序的方法。为此,我创建了一个自定义过滤器并将lambda表达式作为字符串传递,然后使用Python的内置eval()创建真正的lambda:

def my_dictsort(value, by='key', reverse = False):

    if by == 'key':
        sort_by = lambda x: x[0].lower() # assumes key is a str

    elif by == 'value':
        sort_by = lambda x: x[1]

    else:
        sort_by = eval(by)   # assumes lambda string, you should error check

    return sorted(value, key = sort_by, reverse = reverse)
使用此函数,您可以将其注入jinja2环境,如下所示:

env = jinja2.Environment(...)
env.filters['my_dictsort'] = my_dictsort
env.globals['lookup'] = lookup            # queries a database, returns dict
然后从模板中调用它:

{% for key, value in lookup('registers') | my_dict_sort("lambda x:x[1]['address']") %}
{{"""\
    static const unsigned int ADDR_{key} = 0x0{address:04X}; // {name}
""" | format(key = key, address = value['address'], name = value['name']) 
}}
{% endfor %}
输出:

static const unsigned int ADDR_SR = 0x00010; // status register
static const unsigned int ADDR_CMD = 0x00020; // command register

因此,您可以将lambda作为字符串传递,但您必须添加一个自定义筛选器来执行此操作。

我最近不得不处理相同的问题,我必须在我的
Ansible
模板中创建
dict
列表,而这不包括在基本筛选器中

以下是我的解决方法:

def从列表(列表,目标,var\u name=“item”)生成列表:
"""
:param list:输入数据
:param target:在列表的每个项目上应用的转换
:param var_name:lambda中参数的名称,可以根据需要进行更改
:return:包含每个项目目标格式的列表
"""
#我没有把错误处理放在短时间内
#这里我评估lambda模板,插入参数的名称和输出格式
f=eval(“lambda{}:{}”.format(变量名,目标))
返回[f(项目)用于列表中的项目]
#----Ansible过滤器----
类FilterModule(对象):
def过滤器(自):
返回{
“从列表生成列表”:从列表生成列表
}
然后我可以这样使用它:

my_input_list
字符串的
列表
,但它可以与任何内容的
列表一起工作)

这将产生:

  • Python
    中直接调用时:
[{'host':'item1','port':8000},{'host':'item2','port':8000}]
  • 在模板中调用时(在我的示例中是一个yaml文件,但当它返回一个列表时,您可以在转换列表后执行任何操作):

希望这对其他人有所帮助

请不要在模板中使用复杂的逻辑。它们不是为此而设计的。在后端的大多数逻辑操作中,您应该考虑尽可能轻量级的模板。想象一下,在另一个开发者身上发现类似的东西后,你会怎么说?考虑
lambda
创建了一个更难调试的匿名函数。您能推荐一种使用内置过滤器的方法吗?(即,我有一个对象列表,我需要访问每个对象的子对象的属性?)我不认为我试图做的事情在逻辑意义上比内置“映射”允许的更复杂,但我不认为我可以用内置映射做我想做的事。如果可以的话,请告诉我!问题是
lambda作为jinja2过滤器的参数?
答案是
No,您不能在jinja2模板中将常规Python表达式传递给过滤器。似乎是正确的。现在,如果您想对模板中的对象列表使用自定义过滤器,问题是“为什么”。为什么不在将列表传递给模板之前过滤对象?为什么要在将其传递到模板后进行筛选?因为我的筛选器正在处理模板内容,所以我认为它应该在模板中,而不是在控制器中。我的主要对象建议有一个属性contacts,它是联系人对象的列表,每个联系人都有属性name和url。我想构造一个逗号分隔的链接名称表示法(如果合适的话,用“and”表示)。我尝试在Jinja2模板中使用
lambda
,但它似乎没有像您所期望的那样工作。我仍然建议使用控制器中的所有逻辑。您应该只将应该显示的数据传递给模板-只需构造一个
dict
,并将其与
join()
一起使用即可。这不是一个单页应用程序,视图中的任何更改都将在您刷新页面后进行。并考虑有人阅读你的代码并试图理解你为什么这么做。不错的解决方案,但是一个值得考虑的方面是模板的可读性。如果模板的输出可以很容易地从仅仅读取其中的过滤器和命令中辨别出来,那么这对于有效的设计是一个很好的启发。
# I have to put quotes between the <target> parameter because it evaluates as a string
# The variable is not available at the time of the templating so it would fail otherwise
my_variable: "{{ my_input_list | generate_list_from_list(\"{ 'host': item, 'port': 8000 }\") }}"
my_variable: "{{ my_input_list | generate_list_from_list(\"{ 'host': variable, 'port': 8000 }\", var_name="variable") }}"
my_variable:
-    host: item1
     port: 8000
-    host: item2
     port: 8000