如何在Jinja2模板中间接调用宏?

8

我想要做类似于这样的事情:

{% macro obj_type_1 %}
stuff
{% endmacro %}
{% macro obj_type_2 %}
stuff
{% endmacro %}

{{ (obj|get_type)(obj) }}

在这个例子中,get_type是一个过滤器,它将返回obj_type_1obj_type_2--也就是调用obj的宏的名称。我不想用配置输出标记obj,因为现在obj在几个模板中被用作结构数据,根据上下文以不同的标记呈现。
我知道这里的语法有点复杂,但我认为这是因为我想做的事情在Jinja模板中不是立即可能的。我正在尝试用模板替换一些配置生成代码中的大量if/elif/else代码,但这部分似乎是个难点。

所以你基本上是在寻找一种在Jinja2中执行 eval() 的方法? - Blender
接近;我想通过名称调用一个宏。 - Chris R
3个回答

10

你可以创建一个Jinja2过滤器,该过滤器从当前上下文中获取宏(Macro),然后执行该宏。该过滤器如下:

@contextfilter
def call_macro_by_name(context, macro_name, *args, **kwargs):
    return context.vars[macro_name](*args, **kwargs)

如果您的应用程序需要,在查找上下文.vars中的宏之前可以对macro_name进行字符串操作。

这是一个完整的例子:

#!/usr/bin/env python
from jinja2 import Environment, contextfilter

@contextfilter
def call_macro_by_name(context, macro_name, *args, **kwargs):
    return context.vars[macro_name](*args, **kwargs)

template_string = """\
{%- macro MyMacro(item) %}MyMacro({{ item }}){% endmacro -%}
{{ MyMacro('direct') }}
{{ 'MyMacro' | macro('indirect') }}
"""

env = Environment()
env.filters['macro'] = call_macro_by_name
template = env.from_string(template_string)
print(template.render())

打印输出

MyMacro(direct)
MyMacro(indirect)

5
宏可以通过导入字典并使用来简单调用: macros.html
{% macro render_foo(value) %}
HELLO {{ value }}!
{% endmacro %}

my_view.html

{% import "macros.html" as my_macros %}

{% set macro_name = 'render_' + dynamic_content %}
{{ my_macros[macro_name]('world') }}

渲染为:

HELLO world!

0

个人而言,由于get_type被用作调度器,将其实现为一个jinja宏会更加透明,该宏根据obj的类型调用一个专门的宏。这样可以避免它返回可调用的宏,并且同时整合了专门的宏和决定它们何时/如何使用的逻辑。


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