Jinja在可变内容上嵌套渲染

时间:2012-01-14 14:13:59

标签: python jinja2

假设我在变量X中有实际的jinja模板代码。让我们说X的内容是“{{some_other_variable}}”。

如何在呈现内容的同时显示X?

例如,这不起作用:

{{X}}

因为它只是将其渲染为“{{some_other_variable}}”而不是some_other_variable的内容。

我这样做的原因是我有一个网站,其中(可信的)用户可以创建自己可能包含jinja模板代码的帖子。视图页面显示这些帖子,但由于上述问题,直接呈现它们,而不是按照我的意愿替换变量。

7 个答案:

答案 0 :(得分:6)

我知道它有点晚了:)但这是一个不影响模板代码的解决方案:

gcc

试运行:

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

请注意,这不是非常有效,因为必须在每次迭代时从头开始重新分析模板。

答案 1 :(得分:2)

我想到了一个有趣的方法:

  • 设置词典
  • 围绕它包裹DictLoader(但保留对字典的引用)
  • 将带有DictLoader和普通Loader的ChainLoader传递给环境
  • 实施自定义过滤器,将其参数添加到上述词典
  • 使用include指令在当前上下文中调用模板代码

没试过,但它可能会奏效!

答案 2 :(得分:1)

我找不到一个好的方法来做这个嵌套渲染,但是,我可能会尝试建议一个替代方案:

由于用户创建帖子,我想“{{some_other_variable}}”实际上是整个帖子的子字符串,也是一个字符串。

我愿意:

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

然后按正常情况返回{{X}}。那会满足你想要做的吗?

答案 3 :(得分:0)

这就是我提出的,使用环境的finalize选项:

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!'

编辑:

好的,重构我的渲染功能,功能相同,但有点整洁:

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)

答案 4 :(得分:0)

我找到了一种处理模板文件和环境全局变量的方法。

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函数,并且它自己的函数必须在你的构建函数中。

向其发送内容的方式是:render(template, dict(content=post, tags=tags))

其中template = env.get_template('index.html')

答案 5 :(得分:0)

创建新过滤器:

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

答案 6 :(得分:0)

我建议将X设为宏。

import jinja2
tpl = '''
{% macro X(name) -%}
    {{name}}
{%- endmacro %}
Hello {{ X('world') }}!
'''
jinja2.Template(tpl).render()