2013-08-03 2 views
3

Я хочу отобразить шаблон Jinja2 с использованием настраиваемого объекта, реализующего интерфейс __getitem__. Объект реализует ленивую переменную, потому что из него невозможно создать словарь (количество доступных переменных почти бесконечно, поиск значений динамически работает на запрошенном ключе).Lazy variable lookup в шаблонах Jinja

Можно ли сделать шаблон Jinja2 с использованием объекта контекста?

# Invalid code, but I'd like to have such an interface. 
# 

from jinja2 import Template 

class Context(object): 

    def __getitem__(self, name): 
     # Create a value dynamically based on `name` 
     if name.startswith('customer'): 
      key = name[len('customer_'):] 
      return getattr(get_customer(), key) 
     raise KeyError(name) 

t = Template('Dear {{ customer_first }},\n') 
t.render(Context()) 

ответ

1

Я теперь понял, что это (крайне хакерское и уродливое) решение.

t = CustomTemplate(source) 
t.set_custom_context(Context()) 
print t.render() 

Используя следующие замены:

from jinja2.environment import Template as JinjaTemplate 
from jinja2.runtime import Context as JinjaContext 

class CustomContextWrapper(JinjaContext): 

    def __init__(self, *args, **kwargs): 
     super(CustomContextWrapper, self).__init__(*args, **kwargs) 
     self.__custom_context = None 

    def set_custom_context(self, custom_context): 
     if not hasattr(custom_context, '__getitem__'): 
      raise TypeError('custom context object must implement __getitem__()') 
     self.__custom_context = custom_context 

    # JinjaContext overrides 

    def resolve(self, key): 
     if self.__custom_context: 
      try: 
       return self.__custom_context[key] 
      except KeyError: 
       pass 
     return super(CustomContextWrapper, self).resolve(key) 

class CustomTemplate(JinjaTemplate): 

    def set_custom_context(self, custom_context): 
     self.__custom_context = custom_context 

    # From jinja2.environment (2.7), modified 
    def new_context(self, vars=None, shared=False, locals=None, 
        context_class=CustomContextWrapper): 
     context = new_context(self.environment, self.name, self.blocks, 
           vars, shared, self.globals, locals, 
           context_class=context_class) 
     context.set_custom_context(self.__custom_context) 
     return context 

# From jinja2.runtime (2.7), modified 
def new_context(environment, template_name, blocks, vars=None, 
       shared=None, globals=None, locals=None, 
       context_class=CustomContextWrapper): 
    """Internal helper to for context creation.""" 
    if vars is None: 
     vars = {} 
    if shared: 
     parent = vars 
    else: 
     parent = dict(globals or(), **vars) 
    if locals: 
     # if the parent is shared a copy should be created because 
     # we don't want to modify the dict passed 
     if shared: 
      parent = dict(parent) 
     for key, value in iteritems(locals): 
      if key[:2] == 'l_' and value is not missing: 
       parent[key[2:]] = value 
    return context_class(environment, parent, template_name, blocks) 

Может кто-нибудь предложить лучшее решение?

1

Похоже, у вас есть функция, get_customer(), которая возвращает словарь или является объектом?

Почему бы не просто передать это шаблону?

from jinja2 import Template 
t = Template('Dear {{ customer.first }},\n') 
t.render(customer=get_customer()) 

IIRC, Дзиндзя довольно снисходителен ключей, которые не существуют, так customer.bogus_key не врезаться.

+0

'get_customer()' возвращает объект, поэтому я использую 'getattr()' для получения атрибута 'key', который будет' 'первым''. Пример, показанный выше, - это просто, чтобы люди могли видеть, что я пытаюсь сделать. Может быть, не выбрано мудро, так как этот пример действительно будет таким же, как писать 'customer.first' вместо' customer_first'. В реальности я просто хочу вычислить значение на основе ключа. Невозможно/эффективно помещать их в один словарь. –

+0

Hrm. Наверное, я смущен, почему вы не можете передать объект шаблону и получить доступ к его клавишам. –

Смежные вопросы