How can I indirectly call a macro in a Jinja2 template?
jinja2, python
Solution
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)
Problem
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.