Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在Jinja 2中包含文件的顶部插入javascript_Python_Templates_Mako_Jinja2_Genshi - Fatal编程技术网

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

Python 在Jinja 2中包含文件的顶部插入javascript,python,templates,mako,jinja2,genshi,Python,Templates,Mako,Jinja2,Genshi,在Jinja2中,我希望通过运行以下命令,使其看起来能够正常工作: from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader('.')) template = env.get_template('x.html') print template.render() 本质上,目标是通过使用a{%call js()%}/*一些js*/{%endcall%}宏将所有javascri

在Jinja2中,我希望通过运行以下命令,使其看起来能够正常工作:

from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('x.html')
print template.render()
本质上,目标是通过使用a
{%call js()%}/*一些js*/{%endcall%}
宏将所有javascript合并到
标记中


x、 html 我得到以下例外

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

我感觉好像遇到了一个设计问题,即
标记优先于
标记(即宏似乎没有以我期望的方式封装块标记)


我想我的问题很简单:

  • Jinja2能做我正在尝试的事情吗?如果是,怎么做

  • 如果没有,是否有另一个基于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],
        )
    
    当然,这与希望的那样,将脚本放在头部不同,但至少可以方便地合并到一个地方

    但是,解决方案并不完整,因为当您在其中有一个
    {%include“y.html”%}
    时,“y.html”包含一个
    {%js%}
    语句,在include的
    {%js content%}
    语句(即
    x.html
    y.html
    开始之前完全解析)之前调用
    {%js content%}

    我还需要(但尚未)插入常量节点,该节点将具有静态javascript
    try/catch
    ,我表示希望在其中包含该脚本。这不是问题

    我很高兴能取得进展,也很感谢大家的投入

    我打开了相关的问题:


    编辑 解决方案

    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 -->"
    
    类JavascriptBuilderExtension(扩展):
    tags=set(['js'])
    定义初始化(自身、环境):
    super(JavascriptBuilderExtension,self)。\uuu init\uuu(环境)
    environment.extend(jbc=“”,)
    def parse(self,parser):
    “”“分析令牌”“”
    tag=parser.stream.next()
    body=parser.parse_语句(['name:endjs'],drop_=True)
    return nodes.CallBlock(
    self.call_方法(“jbc”、[]、[]、无、无),
    [],[],正文
    ).set_线号(标记线号)
    def_jbc(self,caller=None):
    self.environment.jbc+=“\ntry{%s}catch(e){;};%caller()
    返回“”
    
    完成后,环境将包含一个包含所有Javascript的变量
    jbc
    。我可以通过,例如,
    string.Template
    插入此项


    根据我的评论:

    如果您使用extend而不是 包括你能做到的。但是因为 完全分离 解析和渲染步骤,您将不会 能够更改 在为时已晚的情况下才开始。 此外,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>
    
    {% 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 %}
    

    您可以将其概括为在宏中工作的通用捕获扩展。这是我写的一封信:

    from jinja2 import nodes
    from jinja2.ext import Extension
    
    class CaptureExtension(Extension):
        """
        Generic HTML capture, inspired by Rails' capture helper
    
        In any template, you can capture an area of content and store it in a global
        variable:
    
        {% contentfor 'name_of_variable' %}
            blah blah blah 
        {% endcontentfor %}
    
        To display the result
        {{ name_of_variable }}
    
        Multiple contentfor blocks will append additional content to any previously 
        captured content.  
    
        The context is global, and works within macros as well, so it's useful for letting macros define
        javascript or <head> tag content that needs to go at a particular position
        on the base template.
    
        Inspired by http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
        and http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
        """
        tags = set(['contentfor'])
    
        def __init__(self, environment):
            super(CaptureExtension, self).__init__(environment)
    
        def parse(self, parser):
            """Parse tokens """
            tag = parser.stream.next()
            args = [parser.parse_expression()]
            body = parser.parse_statements(['name:endcontentfor'], drop_needle=True)
            return nodes.CallBlock(self.call_method('_capture', args),[], [], body).set_lineno(tag.lineno)
    
        def _capture(self, name, caller):
            if name not in self.environment.globals:
                self.environment.globals[name] = ''
            self.environment.globals[name] += caller()
            return ""
    
    来自jinja2导入节点的
    
    从jinja2.ext导入扩展
    类CaptureExtension(扩展名):
    """
    通用HTML捕获,灵感来自Rails的捕获助手
    在任何模板中,您都可以捕获内容的一个区域并将其存储在全局模板中
    变量:
    {%contentfor'变量“%”的名称“%”
    废话废话
    {%endcontentfor%}
    显示结果
    {{name_of_variable}}
    多个contentfor块将向任何先前的
    捕获的内容。
    上下文是全局的,并且在宏中也起作用,因此让宏定义上下文非常有用
    需要放在特定位置的javascript或标记内容
    在基础模板上。
    灵感来自http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
    及http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
    """
    tags=set(['contentfor']))
    定义初始化(自身、环境):
    超级(CaptureExtension,self)。\uuuuu初始化\uuuuuuuu(环境)
    def parse(self,parser):
    “”“分析令牌”“”
    tag=parser.stream.next()
    args=[parser.parse_expression()]
    body=parser.parse_语句(['name:endcontentfor'],drop_=True)
    return nodes.CallBlock(self.call_方法(“_capture”,args),[],[]body)。set_lineno(tag.lineno)
    def_捕获(自身、姓名、呼叫者):
    如果名称不在self.environment.globals中:
    self.environment.globals[name]=''
    self.environment.globals[name]+=caller()
    返回“”
    
    Lee Semel的解决方案对我不起作用。我认为globals现在在运行时不受这种修改的影响

    from jinja2 import nodes
    import jinja2
    from jinja2.ext import Extension
    
    class CaptureExtension(Extension):
        """
        Generic HTML capture, inspired by Rails' capture helper
    
        In any template, you can capture an area of content and store it in a global
        variable:
    
        {% capture 'name_of_variable' %}
            blah blah blah 
        {% endcapture %}
        {% capture 'a'  %}panorama{% endcapture %}
    
        To display the result
        {{ captured['name_of_variable'] }}
        {{ captured['a'] }}
    
        The context is global, and works within macros as well, so it's useful for letting macros define
        javascript or <head> tag content that needs to go at a particular position
        on the base template.
    
        Inspired by http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
        and http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
        """
        tags = set(['capture'])
    
        def __init__(self, environment):
            super(CaptureExtension, self).__init__(environment)
            assert isinstance(environment, jinja2.Environment)
            self._myScope = {}
            environment.globals['captured'] = self._myScope
    
        def parse(self, parser):
            """Parse tokens """
            assert isinstance(parser, jinja2.parser.Parser)
            tag = parser.stream.next()
            args = [parser.parse_expression()]
            body = parser.parse_statements(['name:endcapture'], drop_needle=True)
            return nodes.CallBlock(self.call_method('_capture', args),[], [], body).set_lineno(tag.lineno)
    
        def _capture(self, name, caller):
            self._myScope[name] = caller()
            return ""
    
    来自jinja2导入节点的
    
    进口金玉2
    从jinja2.ext导入扩展
    类CaptureExtension(扩展名):
    """
    通用HTML捕获,灵感来自Rails的捕获助手
    在任何模板中,您都可以捕获内容的一个区域并将其存储在全局模板中
    变量:
    {%capture'变量“%”的名称
    废话废话
    {%endcapture%}
    {%capture'一个'%}全景{%endcapture%}
    显示结果
    {{captured['name_of_variable']}
    {{捕获的['a']}
    上下文是全局的,并且在宏中也起作用,因此让宏定义上下文非常有用
    需要放在特定位置的javascript或标记内容
    在基础模板上。
    灵感来自http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
    及http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
    """
    tags=set(['capture']))
    定义初始化(自身、环境):
    超级(CaptureExtens)
    
    <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>
    
    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 -->"
    
    <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>
    
    {% 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 %}
    
    from jinja2 import nodes
    from jinja2.ext import Extension
    
    class CaptureExtension(Extension):
        """
        Generic HTML capture, inspired by Rails' capture helper
    
        In any template, you can capture an area of content and store it in a global
        variable:
    
        {% contentfor 'name_of_variable' %}
            blah blah blah 
        {% endcontentfor %}
    
        To display the result
        {{ name_of_variable }}
    
        Multiple contentfor blocks will append additional content to any previously 
        captured content.  
    
        The context is global, and works within macros as well, so it's useful for letting macros define
        javascript or <head> tag content that needs to go at a particular position
        on the base template.
    
        Inspired by http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
        and http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
        """
        tags = set(['contentfor'])
    
        def __init__(self, environment):
            super(CaptureExtension, self).__init__(environment)
    
        def parse(self, parser):
            """Parse tokens """
            tag = parser.stream.next()
            args = [parser.parse_expression()]
            body = parser.parse_statements(['name:endcontentfor'], drop_needle=True)
            return nodes.CallBlock(self.call_method('_capture', args),[], [], body).set_lineno(tag.lineno)
    
        def _capture(self, name, caller):
            if name not in self.environment.globals:
                self.environment.globals[name] = ''
            self.environment.globals[name] += caller()
            return ""
    
    from jinja2 import nodes
    import jinja2
    from jinja2.ext import Extension
    
    class CaptureExtension(Extension):
        """
        Generic HTML capture, inspired by Rails' capture helper
    
        In any template, you can capture an area of content and store it in a global
        variable:
    
        {% capture 'name_of_variable' %}
            blah blah blah 
        {% endcapture %}
        {% capture 'a'  %}panorama{% endcapture %}
    
        To display the result
        {{ captured['name_of_variable'] }}
        {{ captured['a'] }}
    
        The context is global, and works within macros as well, so it's useful for letting macros define
        javascript or <head> tag content that needs to go at a particular position
        on the base template.
    
        Inspired by http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2
        and http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html
        """
        tags = set(['capture'])
    
        def __init__(self, environment):
            super(CaptureExtension, self).__init__(environment)
            assert isinstance(environment, jinja2.Environment)
            self._myScope = {}
            environment.globals['captured'] = self._myScope
    
        def parse(self, parser):
            """Parse tokens """
            assert isinstance(parser, jinja2.parser.Parser)
            tag = parser.stream.next()
            args = [parser.parse_expression()]
            body = parser.parse_statements(['name:endcapture'], drop_needle=True)
            return nodes.CallBlock(self.call_method('_capture', args),[], [], body).set_lineno(tag.lineno)
    
        def _capture(self, name, caller):
            self._myScope[name] = caller()
            return ""
    
    from jinja2 import Markup, nodes
    from jinja2.ext import Extension
    
    class CaptureExtension(Extension):
        tags = set(['capture'])
    
        def __init__(self, environment):
            super(CaptureExtension, self).__init__(environment)
            environment.globals['captured'] = {}
            self._captured = {}
    
        def parse(self, parser):
            lineno = next(parser.stream).lineno
            args = [parser.parse_expression(), nodes.Const(lineno)]
            body = parser.parse_statements(['name:endcapture'], drop_needle=True)
            return nodes.CallBlock(self.call_method('_capture', args), [], [], body).set_lineno(lineno)
    
        def _capture(self, name, lineno, caller):
            if name not in self._captured:
                self._captured[name] = {}
            self._captured[name][lineno] = caller()
            markup = Markup(''.join(s for s in self._captured[name].values()))
            self.environment.globals['captured'][name] = markup
            return ''