Python 在Jinja 2中包含文件的顶部插入javascript
在Jinja2中,我希望通过运行以下命令,使其看起来能够正常工作: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
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'定义了两次 我感觉好像遇到了一个设计问题,即
块
标记优先于宏
标记(即宏似乎没有以我期望的方式封装块标记)
我想我的问题很简单:
编辑: 我正试图写一个扩展来解决这个问题。我已经完成了一半--使用以下代码:
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%}
我还需要(但尚未)插入常量节点,该节点将具有静态javascripttry/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 ''