Джинджа вложенный рендеринг на переменное содержимое - PullRequest
6 голосов
/ 14 января 2012

Скажем, у меня есть фактический код шаблона jinja в переменной X. Допустим, содержимое X имеет вид "{{some_other_variable}}".

Как я могу отобразить X, одновременно отображая его содержимое?

например, это не работает:

{{X}}

Поскольку он просто отображает это на экране "{{some_other_variable}}", а не на содержимом some_other_variable.

Причина, по которой я так поступаю, заключается в том, что у меня есть сайт, на котором (доверенные) пользователи могут создавать посты, которые сами могут содержать код шаблона jinja.На странице просмотра отображаются эти сообщения, но из-за описанной выше проблемы они отображаются напрямую, а не заменяют переменные, как мне хотелось бы.

Ответы [ 6 ]

6 голосов
/ 30 ноября 2015

Я знаю, что уже немного поздно :), но вот одно решение, не влияющее на код шаблона:

import jinja2
def recursive_render(tpl, values):
     prev = tpl
     while True:
         curr = jinja2.Template(prev).render(**values)
         if curr != prev:
             prev = curr
         else:
             return curr

Тестовый прогон:

>>> recursive_render("Hello {{X}}!", dict(X="{{name}}", name="world"))
u'Hello world!'

Обратите внимание, что это не очень эффективно, так какшаблон должен быть обработан с нуля на каждой итерации.

2 голосов
/ 31 мая 2013

Я подумал об интересном способе сделать это:

  • Настройка словаря
  • Оберните DictLoader вокруг него (но сохраните ссылку на словарь)
  • Передача ChainLoader с DictLoader и обычным загрузчиком в окружающую среду
  • Реализация пользовательского фильтра, который добавляет его параметры в вышеупомянутый словарь
  • Используйте директиву include для вызова кода шаблона в текущем контексте

Не пробовал, но это может сработать!

1 голос
/ 31 мая 2013

Я не смог найти хороший способ сделать этот вложенный рендеринг, однако я могу попытаться предложить альтернативы:

Поскольку пользователь создает сообщения, я думаю, что {{some_other_variable}} "фактически подстрока всего поста, которая также является строкой.

Я бы сделал:

X.replace("{{some_other_variable}}", some_other_variable))

, а затем вернул {{X}}, как обычно.Это удовлетворит то, что вы хотите сделать?

0 голосов
/ 11 января 2018

Создать новый фильтр:

from jinja2 import contextfilter, Markup

@contextfilter
def subrender_filter(context, value):
    _template = context.eval_ctx.environment.from_string(value)
    result = _template.render(**context)
    if context.eval_ctx.autoescape:
        result = Markup(result)
    return result

env = Environment()
env.filters['subrender'] = subrender_filter

И затем использовать его в шаблоне:

{{ "Hello, {{name}}"|subrender }}
0 голосов
/ 15 мая 2017

Я нашел способ работы с файлами шаблонов и глобальными переменными среды.

def render(template, values):
     prev = template.render(**values)
     while True:
         curr = Template(prev).render(siteinfo=config, menus=menus, blended_header=header, authors=authors, buildinfo=buildinfo, **values)
         if curr != prev:
             prev = curr
         else:
             return curr

В этой версии вы должны передать глобальные переменные среды в функцию рендеринга внутри этой функции и функцию, которую она самадолжен быть в вашей функции сборки.

Способ отправки контента на него: render(template, dict(content=post, tags=tags))

, где template = env.get_template('index.html')

0 голосов
/ 10 февраля 2016

Это то, что я придумал, используя опцию finalize для Environment:

def render (tpl, args):
    scope = {}
    scope['env'] = jinja2.Environment (finalize=lambda x: scope['env'].from_string (x).render (**args) if isinstance(x, str) and '{{' in x else x)
    scope['env'].filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
    return scope['env'].from_string (tpl).render (**args)

Это немного уродливо, поэтому улучшения приветствуются:)

Но он выполняет рекурсивный рендеринг, как запрашивал OP, и не останавливается до тех пор, пока на выходе не останется {{

Пример сеанса

>>> import jinja2
>>> def render (tpl, args):
...         scope = {}
...         scope['env'] = jinja2.Environment (finalize=lambda x: scope['env'].from_string (x).render (**args) if isinstance(x, str) and '{{' in x else x)
...         scope['env'].filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
...         return scope['env'].from_string (tpl).render (**args)
... 
>>> render("this {{ outer_var }} wokring!", {"outer_var":"{{ inner_var }}", "inner_var":"{{ final_step }}", "final_step":"is"})
u'this is wokring!'

EDIT:

Хорошо, немного изменил мою функцию рендеринга, функционально то же самое, но немного более аккуратно:

def render (tpl, args):
    @jinja2.environmentfunction
    def finalize (env, value):
        if isinstance(value, (str, unicode)) and '{{' in value:
            return env.from_string (value).render (args)
        return value
    env = jinja2.Environment (finalize=finalize)
    env.filters['q'] = lambda value: re.sub (r'"', r'\\"', value)
    return env.from_string (tpl).render (args)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...