Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

python

jinja2

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.

like image 569
Chris R Avatar asked May 17 '12 04:05

Chris R


People also ask

What is Jinja2 macro?

Macros within Jinja2 are essentially used like functions with Python and are great for repeating configs with identical structures (think interface configurations, ASA object-groups, etc).

Is Jinja2 dynamic?

Jinja supports dynamic inheritance and does not distinguish between parent and child template as long as no extends tag is visited.


1 Answers

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)
like image 190
Matt Liberty Avatar answered Sep 28 '22 03:09

Matt Liberty