2012-05-17 6 views
7

Próbuję zrobić coś takiego:Jak mogę pośrednio wywołać makro w szablonie Jinja2?

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

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

W tym przykładzie get_type jest filtrem, który zwróciłby obj_type_1 lub obj_type_2 - czyli nazwa makra wezwać obj. Nie chcę oznaczyć obj z danymi wyjściowymi konfiguracji, ponieważ teraz obj jest używany w kilku szablonach jako dane strukturalne, które mają być renderowane z różnymi znacznikami w zależności od kontekstu.

Wiem, że składnia tutaj jest nieco torturowana, ale myślę, że to dlatego, że to, co chcę zrobić, nie jest natychmiast możliwe w szablonach Jinja. Próbuję zastąpić wielką cholerę tego/elif/else crap w kodzie generowania konfiguracji za pomocą szablonów, ale ten bit wydaje się być punktem krytycznym.

+0

Więc w zasadzie szuka sposobu na 'eval()' w Jinja2? – Blender

+0

Blisko; Chcę wywołać makro według nazwy. –

Odpowiedz

0

Osobiście, ponieważ get_type jest używany jako dyspozytor, bardziej przejrzyste byłoby zaimplementowanie go jako makro jinja, które wywołuje wyspecjalizowane makro w oparciu o typ obiektu. Eliminuje to potrzebę zwracania makro wywoływalnego, a jednocześnie konsoliduje wyspecjalizowane makra i logikę, która dyktuje, w jaki sposób/kiedy są używane.

8

Można utworzyć filtr Jinja2, który pobiera makro z bieżącego kontekstu, a następnie ocenia makro. Filtr jest:

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

Jeśli aplikacja wymaga, można wykonać ciąg manipulacji na macro_name przed patrząc makra w context.vars.

Oto pełny przykład:

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

która drukuje

MyMacro(direct) 
MyMacro(indirect) 
2

Makra można po prostu nazywany przez użycie import dict:

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

renderowanie jak:

HELLO world!