在Jinja 2中的包含文件顶部插入javascript

Bri*_*unt 10 python templates mako genshi jinja2

在Jinja2中,我希望通过运行以下内容来实现它的运行:

from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('x.html')
print template.render()
Run Code Online (Sandbox Code Playgroud)

本质上,目标是<head>通过使用{% call js() %} /* some js */ {% endcall %}宏来将所有javascript合并到标签中.


x.html

<html>
<head>
  <script type="text/javascript>
  {% block head_js %}{% endblock %}
  </script>
  </head>
<body>
  {% include "y.html" %}
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

y.html

{% macro js() -%}
    // extend head_js
    {%- block head_js -%}
    {{ super() }}
    try { {{ caller() }} } catch (e) {
       my.log.error(e.name + ": " + e.message);
    }
    {%- endblock -%}
{%- endmacro %}

Some ... <div id="abc">text</div> ...

{% call js() %}
    // jquery parlance:
    $(function () {
        $("#abc").css("color", "red");
    });
{% endcall %}
Run Code Online (Sandbox Code Playgroud)

预期结果

当我通过jinja2运行X.html时,我希望结果如下:

<html>
<head>
  <script type="text/javascript>
  try { {{ $("#abc").css("color", "red"); }} } catch (e) {
       usf.log.error(e.name + ": " + e.message);
    }
  </script>
  </head>
<body>
      Some ... <div id="abc">text</div> ...
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

实际结果

实际结果并不令人鼓舞.我得到了几种可能有启发性的错误,例如:

TypeError:宏'js'不带关键字参数'caller'

或者,当我尝试添加另一个基础宏如

{% macro js2() -%}
{%- block head_js -%}
//     ... something
{%- endblock -%}
{%- endmacro %}
Run Code Online (Sandbox Code Playgroud)

我得到以下异常

jinja2.exceptions.TemplateAssertionError:块'head_js'定义了两次

我觉得好像我遇到了关于block标签在macro标签上的优先级的设计问题(即宏似乎没有以我期望的方式封装块标签).


我想我的问题很简单:

  1. Jinja2可以做我正在尝试的事吗?如果是这样,怎么样?

  2. 如果没有,是否有另一个基于Python的模板引擎支持这种模式(例如mako,genshi等),这在Google App Engine中可以正常工作

感谢您的阅读 - 感谢您的投入.

布赖恩


编辑:

我正在尝试编写扩展来解决此问题.我在那里 - 使用以下代码:

from jinja2 import nodes, Environment, FileSystemLoader
from jinja2.ext import Extension

class JavascriptBuilderExtension(Extension):
    tags = set(['js', 'js_content'])

    def __init__(self, environment):
        super(JavascriptBuilderExtension, self).__init__(environment)
        environment.extend(
            javascript_builder_content = [],
        )

    def parse(self, parser):
        """Parse tokens """
        tag = parser.stream.next()
        return getattr(self, "_%s" % str(tag))(parser, tag)

    def _js_content(self, parser, tag):
        """ Return the output """
        content_list = self.environment.javascript_builder_content
        node = nodes.Output(lineno=tag.lineno)
        node.nodes = []

        for o in content_list:
            print "\nAppending node: %s" % str(o)
            node.nodes.extend(o[0].nodes)
        print "Returning node: %s \n" % node
        return node

    def _js(self, parser, tag):
        body = parser.parse_statements(['name:endjs'], drop_needle=True)
        print "Adding: %s" % str(body)
        self.environment.javascript_builder_content.append(body)
        return nodes.Const('<!-- Slurped Javascript -->')

env = Environment(
    loader      = FileSystemLoader('.'),
    extensions  = [JavascriptBuilderExtension],
    )
Run Code Online (Sandbox Code Playgroud)

这使得将Javascript添加到模板的末尾变得简单......例如

<html>
<head></head>
<body>
    {% js %}
    some javascript {{ 3 + 5 }}
    {% endjs %}
    {% js %}
    more {{ 2 }}
    {% endjs %}

<script type="text/javascript">
{% js_content %}
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

运行env.get_template('x.html').render()将产生一些有启发性的评论和预期的输出:

<html>
<head>
  <script type="text/javascript>
  </script>
  </head>
<body>
    <!-- Slurped Javascript -->
    <!-- Slurped Javascript -->
<script type="text/javascript">
    some javascript 8
    more 2
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

当然,这与脚本中的脚本不一样,但至少它可以方便地合并到一个地方.

但是,解决方案并不完整,因为当你有一个{% include "y.html" %}"y.html"包含一个{% js %}语句时,{% js_content %}在include {% js %}语句之前调用gets (即x.htmly.html开始之前完全解析).

我还需要,但还没有,插入具有静态javascript的常量节点,try/catch我表示我想要在那里.这不是问题.

我很高兴能取得进步,我很感激投入.

我打开了相关的问题:Jinja2编译扩展后包含


编辑

class JavascriptBuilderExtension(Extension):
    tags = set(['js'])

    def __init__(self, environment):
        super(JavascriptBuilderExtension, self).__init__(environment)
        environment.extend(jbc = "",)

    def parse(self, parser):
        """Parse tokens """
        tag = parser.stream.next()
        body = parser.parse_statements(['name:endjs'], drop_needle=True)
        return nodes.CallBlock(
            self.call_method('_jbc', [], [], None, None),
            [], [], body
        ).set_lineno(tag.lineno)

    def _jbc(self, caller=None):
        self.environment.jbc += "\ntry { %s } catch (e) { ; };" % caller()
        return "<!-- Slurped -->"
Run Code Online (Sandbox Code Playgroud)

完成后,环境将包含一个包含jbc所有Javascript 的变量.我可以插入这个,例如,string.Template.


Wol*_*lph 5

从我的评论:

如果您使用extend而不是include,则可以执行此操作.但是由于解析和渲染步骤之间的完全分离,您将无法更改父范围的上下文,直到它为时已晚.此外,Jinja上下文应该是不可变的.

例:

base.html文件

<html>
   <head>
      {% block head %}

      <title>{% block title %}This is the main template{% endblock %}</title>

      <script type="text/javascript">
      {% block head_js %}
      $(function () {
        $("#abc").css("color", "red");
      });
      {% endblock %}
      </script>

      {% endblock head_js %}
   </head>
   <body>
      {% block body %}
      <h1>{% block body_title %}This is the main template{% endblock body_title %}</h1>

      {% endblock body %}
   </body>
 </html>
Run Code Online (Sandbox Code Playgroud)

some_page.html

{% block title %}This is some page{% endblock title %}

{% block head_js %}
{{ super() }}
try { {{ caller() }} } catch (e) {
   my.log.error(e.name + ": " + e.message);
}        // jquery parlance:
{% endblock head_js %}
Run Code Online (Sandbox Code Playgroud)