首页 文章

Jinja在可变内容上嵌套渲染

提问于
浏览
6

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

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

例如,这不起作用:

{}

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

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

6 回答

  • 2

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

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

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

  • 1

    我想到了一个有趣的方法来做到这一点:

    • 设置字典

    • 在它周围缠绕一个DictLoader(但保留对字典的引用)

    • 将带有DictLoader和普通Loader的ChainLoader传递给环境

    • 实现自定义过滤器,将其参数添加到上述字典中

    • 使用 include 指令在当前上下文中调用模板代码

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

  • 0

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

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

    我会做:

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

    然后按正常方式返回{} . 那会满足你想做的吗?

  • 6

    这就是我想到的,使用环境的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)
    
  • 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')

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

相关问题