简体   繁体   中英

How can I indirectly call a macro in a Jinja2 template?

I'm trying to do something like this:

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

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

In this example, get_type is a filter that would return obj_type_1 or obj_type_2 -- ie, the name of the macro to call for obj . I don't want to mark up obj with configuration output because right now obj is used in several templates as structural data, to be rendered with different markup depending on the context.

I know the syntax here is a bit tortured, but I think that's because what I want to do isn't immediately possible in Jinja templates. I'm trying to replace a big damn schwack of if/elif/else crap in some config generation code with templates, but this bit seems to be a sticking point.

You can create a Jinja2 filter which gets the Macro from the current context and then evaluates the Macro. The filter is:

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

If your application requires, you can perform string manipulation on macro_name before looking up the Macro in context.vars.

Here is a full example:

#!/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())

which prints

MyMacro(direct)
MyMacro(indirect)

Macros can simply called by import dict usage:

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') }}

render as:

HELLO world!

Personally, since get_type is used as a dispatcher, it would be more transparent to implement it as a jinja macro that calls a specialized macro based on the type of obj. This removes the need for it to return a callable macro and, at the same time, consolidates the specialized macros and the logic that dictates how/when they are used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM