【问题标题】:Is inline code allowed in Jinja templates?Jinja 模板中是否允许内联代码?
【发布时间】:2011-09-25 08:31:05
【问题描述】:

我在我的网站上是 using Jinja,我喜欢它。

我遇到了一个简单的需求。如何显示今天的日期? 有没有办法在 Jinja 模板中内联一些 Python 代码?

import datetime
now = datetime.datetime.utcnow()
print now.strftime("%Y-%m-%d %H:%M")

This article says no,但建议使用宏或过滤器?

真的吗?我们必须求助于这一切吗?好的,在这种情况下会是什么样子?

【问题讨论】:

    标签: python jinja2


    【解决方案1】:

    不,没有办法将 Python 内联到 Jinja。但是,您可以通过扩展模板引擎的Environment 或可用于所有模板的global namespace 来添加Jinja 知道的结构。或者,您可以添加一个过滤器来格式化日期时间对象。

    Flask 将 Jinja2 环境存储在 app.jinja_env 上。您可以通过直接添加到此字典或使用 @app.context_processor 装饰器来将新上下文注入环境。

    无论您选择何种路径,都应该在您设置应用程序时、在您处理任何请求之前完成。 (有关如何设置过滤器的一些goodexamples,请参阅网站的 sn-ps 部分 - the docs 包含添加到全局变量的一个很好的示例)。

    【讨论】:

      【解决方案2】:

      当前的答案几乎适用于所有情况。但是,在极少数情况下,您希望在模板中包含 python 代码。就我而言,我想用它来预处理一些乳胶文件,我更愿意将生成表值、绘图等的 python 代码保留在它自己的乳胶文件中。

      所以我做了一个 Jinja2 扩展,它添加了一个新的“py”块,允许在模板中编写 python 代码。请记住,我必须采取一些有问题的变通方法才能使其正常工作,因此我不能 100% 确定它在哪些情况下会失败或出现意外行为。

      这是一个示例模板。

      Foo was given to the template
      foo: {{ foo }}
      
      Bar was not, so it is missing
      bar is missing: {{ bar == missing }}
      
      {% py %}
          # Normal python code in here
          # Excess indentation will be removed.
          # All template variables are accessible and can be modified.
          import numpy as np
          a = np.array([1, 2])
          m = np.array([[3, 4], [5, 6]])
          bar = m @ a * foo
      
          # It's also possible to template the python code.
          {% if change_foo %}
          foo = 'new foo value'
          {% endif %}
      
          print("Stdio is redirected to the output.")
      {% endpy %}
      
      Foo will have the new value if you set change_foo to True
      foo: {{ foo }}
      
      Bar will now have a value.
      bar: {{ bar }}
      
      {% py %}
          # The locals from previous blocks are accessible.
          m = m**2
      {% endpy %}
      m:
      {{ m }}
      

      如果我们将模板参数设置为foo=10, change_foo=True,输出是:

      Foo was given to the template
      foo: 10
      
      Bar was not, so it is missing
      bar is missing: True
      
      Stdio is redirected to the output.
      
      
      Foo will have the new value if you set change_foo to True
      foo: new foo value
      
      Bar will now have a value.
      bar: [110 170]
      
      
      m:
      [[ 9 16]
       [25 36]]
      

      带有运行示例的 main 函数的扩展。

      from jinja2 import Environment, PackageLoader, nodes
      from jinja2.ext import Extension
      from textwrap import dedent
      from io import StringIO
      import sys
      import re
      import ctypes
      
      
      def main():
          env = Environment(
              loader=PackageLoader('python_spike', 'templates'),
              extensions=[PythonExtension]
          )
      
          template = env.get_template('emb_py2.txt')
          print(template.render(foo=10, change_foo=True))
      
      
      var_name_regex = re.compile(r"l_(\d+)_(.+)")
      
      
      class PythonExtension(Extension):
          # a set of names that trigger the extension.
          tags = {'py'}
      
          def __init__(self, environment: Environment):
              super().__init__(environment)
      
          def parse(self, parser):
              lineno = next(parser.stream).lineno
              body = parser.parse_statements(['name:endpy'], drop_needle=True)
              return nodes.CallBlock(self.call_method('_exec_python',
                                                      [nodes.ContextReference(), nodes.Const(lineno), nodes.Const(parser.filename)]),
                                     [], [], body).set_lineno(lineno)
      
          def _exec_python(self, ctx, lineno, filename, caller):
              # Remove access indentation
              code = dedent(caller())
      
              # Compile the code.
              compiled_code = compile("\n"*(lineno-1) + code, filename, "exec")
      
              # Create string io to capture stdio and replace it.
              sout = StringIO()
              stdout = sys.stdout
              sys.stdout = sout
      
              try:
                  # Execute the code with the context parents as global and context vars and locals.
                  exec(compiled_code, ctx.parent, ctx.vars)
              except Exception:
                  raise
              finally:
                  # Restore stdout whether the code crashed or not.
                  sys.stdout = stdout
      
              # Get a set of all names in the code.
              code_names = set(compiled_code.co_names)
      
              # The the frame in the jinja generated python code.
              caller_frame = sys._getframe(2)
      
              # Loop through all the locals.
              for local_var_name in caller_frame.f_locals:
                  # Look for variables matching the template variable regex.
                  match = re.match(var_name_regex, local_var_name)
                  if match:
                      # Get the variable name.
                      var_name = match.group(2)
      
                      # If the variable's name appears in the code and is in the locals.
                      if (var_name in code_names) and (var_name in ctx.vars):
                          # Copy the value to the frame's locals.
                          caller_frame.f_locals[local_var_name] = ctx.vars[var_name]
                          # Do some ctypes vodo to make sure the frame locals are actually updated.
                          ctx.exported_vars.add(var_name)
                          ctypes.pythonapi.PyFrame_LocalsToFast(
                              ctypes.py_object(caller_frame),
                              ctypes.c_int(1))
      
              # Return the captured text.
              return sout.getvalue()
      
      if __name__ == "__main__":
          main()
      

      【讨论】:

        【解决方案3】:

        您可以添加到可以从 Jinja 模板访问的global variables。你可以把你自己的函数定义放在那里,它可以做任何你需要的事情。

        【讨论】:

          猜你喜欢
          • 2017-08-03
          • 1970-01-01
          • 1970-01-01
          • 2018-09-15
          • 2012-02-13
          • 2013-09-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多