Как я могу предотвратить циклические {% include %} звонки в шаблонах Jinja2

У меня есть приложение Django SaaS с пользователями, которые создают свои собственные шаблоны Jinja2 (в необычно изолированной среде для тех, кто только что выползли), которые сохраняются в базе данных. у меня есть template_type поле, отмечая, является ли данный шаблон "включенным" или "полным шаблоном" (полный шаблон может, конечно, включать "включает"). Проблема в том, что пользователь мог поставить {% include "foo" %} в шаблон под названием "bar", а также {% include "bar" %} в "foo" шаблон, в результате чего RuntimeError: maximum recursion depth exceeded тип ситуации, которая не будет хорошей для производительности.

Есть хороший способ справиться с этой ситуацией, которая не включает регулярное выражение проверки (например, r'\{%\s+include') проверка на наличие включений во время создания пользовательского шаблона ("Убедитесь, что рекурсивный импорт никогда не попадет в базу данных, иначе ваш сервер взорвется" не совсем подходит мне).

Моя неудачная попытка

Я начал с использования пользовательского загрузчика, который содержит только пользовательские "include"::

def get_source(self, environment, template):
    """
    Returns the source of the template or raises ``TemplateNotFound``.
    This loader is for use with an environment which intends to load each
    template from a string, since only includes are loaded from here.
    """
    try:
        template = self.snippets[template]
    except KeyError:
        raise jinja2.TemplateNotFound(
            template, message=u'Snippet "{s}" was not found'.format(s=template)
        )
    else:
        source = template['source']
        return (source, None, lambda: True)

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

1 ответ

Решение

Для разбора шаблонов и проверки включений я использую этот код:

    ast = env.parse(template_text)
    for each in meta.find_referenced_templates(ast) :        # find the (% includes %}
Другие вопросы по тегам