【问题标题】:Jinja nested rendering on variable contentJinja 在可变内容上嵌套渲染
【发布时间】:2012-01-14 14:13:58
【问题描述】:

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

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

例如,这不起作用:

{{ X }}

因为它只是将它呈现到屏幕“{{ some_other_variable }}”而不是 some_other_variable 的内容。

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

【问题讨论】:

    标签: python jinja2


    【解决方案1】:

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

    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】:

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

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

      没试过,不过应该可以!

      【讨论】:

      • 自定义过滤器可以做任何事情,你甚至不需要步骤 1 到 3。你可以通过 jinja 传递变量。有没有其他方法可以通过 jinja 处理变量替换,而不是像这样手动。
      • 嗯?过滤器只是传递模板内容并将它们放入模板字典中,以便 Jinja 可以以通常的方式处理它们。棘手的部分是获取 Jinja 的上下文(以便您可以使用它来处理模板)。另外,您为什么不想使用自定义过滤器?
      【解决方案3】:

      创建新过滤器:

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

      【讨论】:

        【解决方案4】:

        这是我想出的,使用环境的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)
        

        【讨论】:

          【解决方案5】:

          我找不到进行这种嵌套渲染的好方法,但是,我可能会尝试提出替代方案:

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

          我愿意:

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

          然后正常返回 {{X}}。这能满足你想做的事吗?

          【讨论】:

          • Jinja2 替换会比这更好。 {{ X|replace("{{Hello}}", "Goodbye") }} 我需要更多的原生解决方案。
          • 我当然不是最有经验的人。我很高兴知道是否有任何特殊原因使 jinja2 替换得更好。另外,您所说的“本地”是什么意思?尽可能靠近 Jinja2?
          • 哦,是的,我的意思是尽可能接近 Jinja2。我只是在等待更通用的解决方案,通过它我可以在这些场景中添加整个 jinja2 功能(不仅仅是变量替换)。否则,我会为变量替换提供赏金。
          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

            我建议将 X 设为宏。

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

            【讨论】:

              猜你喜欢
              • 2023-03-21
              • 2020-10-04
              • 2015-05-15
              • 1970-01-01
              • 2022-01-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多