在 Jinja 2 中包含文件的顶部插入 javascript
在 Jinja2 中,我希望通过运行以下内容来正常工作:
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('x.html')
print template.render()
本质上,目标是使用 aa { 将所有 javascript 合并到
宏。 标记中% call js() %} /* 一些 js */ {% endcall %}
x.html
<html>
<head>
<script type="text/javascript>
{% block head_js %}{% endblock %}
</script>
</head>
<body>
{% include "y.html" %}
</body>
</html>
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 %}
预期结果
当我通过 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>
实际结果
实际结果并不令人鼓舞。我遇到了几种可能具有启发性的错误,例如:
类型错误:宏“js”不接受关键字参数“调用者”
,或者,当我尝试添加另一个基础宏时,例如
{% macro js2() -%}
{%- block head_js -%}
// ... something
{%- endblock -%}
{%- endmacro %}
我收到以下异常
jinja2.exceptions.TemplateAssertionError:块“head_js”定义了两次
我觉得好像遇到了一个关于 block
标记相对于 macro
标记的优先级的设计问题(即宏似乎没有按照我期望的方式封装块标签)。
我想我的问题很简单:
Jinja2 可以做我正在尝试的事情吗?如果是这样,怎么办?
如果没有,是否有另一个基于 Python 的模板引擎支持这种模式(例如 mako、genshi 等),并且可以在 Google App Engine 中正常工作
感谢您的阅读 - 我感谢您的意见。
Brian
编辑:
我正在尝试编写一个扩展来解决这个问题。我已经完成了一半——使用以下代码:
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],
)
这使得将 Javascript 添加到模板末尾变得简单...例如
<html>
<head></head>
<body>
{% js %}
some javascript {{ 3 + 5 }}
{% endjs %}
{% js %}
more {{ 2 }}
{% endjs %}
<script type="text/javascript">
{% js_content %}
</script>
</body>
</html>
运行 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>
当然,这与所希望的将脚本放在头部不同,但至少它可以方便地合并到一个地方。
但是,该解决方案并不完整,因为当您有一个 {% include "y.html" %}
时,其中“y.html”包含一个 {% js %}
code> 语句,{% js_content %}
在包含的 {% js %}
语句之前被调用(即 x.html
被完全解析)在 y.html
开始之前)。
我还需要(但尚未)插入具有静态 javascript try/catch
的常量节点,我表示我希望将其插入其中。这不是问题。
我很高兴能取得进步,也很感谢大家的意见。
我已经打开了相关问题: Jinja2 编译扩展包含后
In Jinja2, I would like the following to work as it looks like it should, by running:
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('x.html')
print template.render()
Essentially the objective is to coalesce all the javascript into the <head>
tags by using a a {% call js() %} /* some js */ {% endcall %}
macro.
x.html
<html>
<head>
<script type="text/javascript>
{% block head_js %}{% endblock %}
</script>
</head>
<body>
{% include "y.html" %}
</body>
</html>
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 %}
Expected result
When I run X.html through jinja2, I would expect the result to be:
<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>
Actual result
The actual results are not encouraging. I get a couple types of potentially illuminating errors, e.g.:
TypeError: macro 'js' takes no keyword argument 'caller'
or, when I try adding another basis macro such as
{% macro js2() -%}
{%- block head_js -%}
// ... something
{%- endblock -%}
{%- endmacro %}
I get the following exception
jinja2.exceptions.TemplateAssertionError: block 'head_js' defined twice
I feel as though I am running into a design issue regarding the precedence of the block
tags over the macro
tags (i.e. macros do not seem to encapsulate block tags in the way I expect).
I suppose my questions are quite simple:
Can Jinja2 do what I am attempting? If so, how?
If not, is there another Python based templating engine that does support this sort of pattern (e.g. mako, genshi, etc.), which would work without issue in Google App Engine
Thank you for reading - I appreciate your input.
Brian
Edit:
I'm trying to write an extension to resolve this problem. I'm halfway there -- using the following code:
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],
)
This makes it simple to add Javascript to the end of a template ... e.g.
<html>
<head></head>
<body>
{% js %}
some javascript {{ 3 + 5 }}
{% endjs %}
{% js %}
more {{ 2 }}
{% endjs %}
<script type="text/javascript">
{% js_content %}
</script>
</body>
</html>
Running env.get_template('x.html').render()
will result in some illuminating comments and the expected output of:
<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>
Of course, this isn't the same as having the script in the head, as hoped, but at least it's conveniently coalesced into one place.
However, the solution is not complete because when you have a {% include "y.html" %}
in there, where "y.html" includes a {% js %}
statement, the {% js_content %}
gets called before the include's {% js %}
statement (i.e. x.html
is fully parsed before y.html
starts).
I also need to, but have not yet, inserted constant nodes that would have the static javascript try/catch
, which I indicated I wanted to have in there. This is not an issue.
I'm pleased to be making progress, and I'm grateful for input.
I have opened the related question: Jinja2 compile extension after includes
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
从我的评论来看:
示例:
base.html
some_page.html
From my comment:
Example:
base.html
some_page.html
您可以将其概括为在宏中工作的通用捕获扩展。这是我写的:
You can generalize this into a generic capture extension that works within macros. Here's one I wrote:
Lee Semel 的解决方案对我不起作用。我认为全局变量现在在运行时不受这种修改的影响。
The solution by Lee Semel did not work for me. I think globals are protected from this kind of modification at runtime now.
上面的答案几乎回答了我的查询(我想将不同的 JavaScript 部分全部放在一个地方 - 底部),接受使用“+=”变量,它将捕获相互附加,导致刷新问题。捕获最终会产生所有内容的多个副本,并导致各种问题,具体取决于刷新的次数。
我通过使用字典中标签的行号来解决这个问题,以确保捕获只完成一次。这种方法的一个小缺点是每次遇到捕获标记时都需要重建全局。
不过对我来说效果很好。
The answers above nearly answered my query (I wanted to put disparate bits of JavaScript all in one place - the bottom), accept using the '+=' variety which appends captures to each other caused problems on refresh. The capture would end up with multiple copies of everything and caused all sorts of issues depending on how many times refresh was hit.
I worked around this by using the line number of the tag in a dictionary to ensure captures are only done once. The one minor disadvantage of this approach is the global needs to be rebuilt every time a capture tage is encountered.
Works well for me though.
完成后,环境将包含一个变量
jbc
,其中包含所有Javascript。我可以通过例如string.Template
插入它。此答案是作为问题编辑发布的questions/4292630/insert-javascript-at-top-of-include-file-in-jinja-2">在 Jinja 2 中包含文件的顶部插入 javascript 由 OP Brian M. Hunt 根据 CC BY-SA 2.5。
After completed, the environment will contain a variable
jbc
that has all the Javascript. I can insert this via, for example,string.Template
.This answer was posted as an edit to the question Insert javascript at top of including file in Jinja 2 by the OP Brian M. Hunt under CC BY-SA 2.5.