lambda作为jinja2过滤器的参数?

时间:2014-06-19 15:33:50

标签: 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然后将其作为变量传递给模板,但我宁愿能够在模板中创建它。

3 个答案:

答案 0 :(得分:7)

不,你不能传递一般Python表达式来过滤Jinja2模板

混淆来自于jinja2模板在许多方面与Python语法类似,但您应将其视为具有完全独立语法的代码。

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

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

答案 1 :(得分:6)

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

registers = dict(
    CMD = dict(
        address = 0x00020,
        name = 'command register'),
    SR = dict(
        address = 0x00010,
        name = 'status register'),
)

我想循环注册dict,但按地址排序。所以我需要一种方法来按地址排序'领域。为此,我创建了一个自定义过滤器并将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作为字符串传递,但是您必须添加自定义过滤器才能执行此操作。

答案 2 :(得分:0)

我最近不得不处理相同的问题,我必须在我的list模板中创建dict的{​​{1}},但基本过滤器中没有

这是我的工作习惯:

Ansible

然后我就可以通过这种方式使用它:

({def generate_list_from_list(list, target, var_name="item"): """ :param list: the input data :param target: the applied transformation on each item of the list :param var_name: the name of the parameter in the lambda, to be able to change it if needed :return: A list containing for each item the target format """ # I didn't put the error handling to keep it short # Here I evaluate the lambda template, inserting the name of the parameter and the output format f = eval("lambda {}: {}".format(var_name, target)) return [f(item) for item in list] # ---- Ansible filters ---- class FilterModule(object): def filters(self): return { 'generate_list_from_list': generate_list_from_list } my_input_list的{​​{1}},但它可以与list的任何东西一起使用)

string

或者如果我想重命名lambda参数:

list

这将输出:

  • # 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") }}"
  • 在模板中调用时(在我的示例中是一个yaml文件,但是当它返回一个列表时,一旦转换列表,您就可以做任何您想做的事情):
Python

希望这对某人有帮助