Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jinja2: macro selecting macro or dynamic macro calls

I have a list of namedtuples I'm going through, each slightly differing in rendering requirements, so I want to call the proper macro based on an attribute. What I have is this:

{% macro format_item(item) %}
    {% if item.type_of == 'a' %}
        {{ format_a(item) }}
    {% elif item.type_of == 'b' %}
        {{ format_b(item) }}
    {% elif item.type_of == 'c'%}
        {{ format_c(item) }}
    {% elif item.type_of == 'd'%}
        {{ format_d(item) }}
    {% else %}
        {{ format_general(item) }}
    {% endif %}
{% endmacro %}

but what I want is to something like:

...iterating through list of items
{{ call macro based off item.type_of }}

at this point in regular python I'd do something like

getattr(object_with_method_to_produce_templates, item)

but haven't figured out a way to use the attr filter effectively (if I can use it properly in this situation at all).

I've found flask.get_template_attribute looking elsewhere which might be interesting (if I can instead just do it all ahead of time and send a precalculated and preformatted item to the template). Maybe too much and beyond what I want to do at this time.

What is a better way to call from a varied listing of macros instead of a list of if then's that might get rather large in the future? Seems like a common question, but I have not stumbled on the exact answer I'm looking for.

EDIT:

I added this to what I was doing, trying to generate a callable macro as part of the item I want to render

from flask import get_template_attribute
from jinja2 import Template
test_template = Template('{% macro test_macro(item) %}<div id="test-div">sent to me: {{ item }}</div>{% endmacro %}')

...in item generation...

 template = get_template_attribute(test_template, 'test_macro')

...in template...iterate items then for each item

{{ item.template("testing this method") }}

which sort of works but only generates the string letter for letter, not as a regular macro would(i.e. the divs aren't rendered as divs, only as text).

<div id="test-div">sent to me: testing this method</div>

So I need to give Template some context, or something this is closer to what was aiming for but doesn't seem right.

EDIT2:

{{ item.template("testing this method")|safe }}

returns what I was looking for, so this is passable, I might be able to bypass the namedtuple arrangement I had and just pass a macro with...more working on it I suppose. Is this optimal/preferable or a mess though?

like image 905
blueblank Avatar asked Dec 24 '12 02:12

blueblank


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)

See the full answer here.

like image 115
Matt Liberty Avatar answered Nov 15 '22 05:11

Matt Liberty