В Jinja2 я хотел бы, чтобы следующее работало так, как должно, запустив:
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('x.html')
print template.render()
По сути, цель состоит в том, чтобы объединить весь javascript в теги <head>
с помощью макроса {% call js() %} /* some js */ {% endcall %}
.
х.html
<html>
<head>
<script type="text/javascript>
{% block head_js %}{% endblock %}
</script>
</head>
<body>
{% include "y.html" %}
</body>
</html>
у.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 %}
Ожидаемый результат
Когда я запускаю X.html через jinja2, я ожидаю, что результат будет таким:
<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>
Фактический результат
Фактические результаты не радуют. Я получаю пару типов потенциально освещающих ошибок, например:
TypeError: макрос 'js' не принимает аргумент ключевого слова 'вызывающий'
или, когда я пытаюсь добавить другой базовый макрос, такой как
{% macro js2() -%}
{%- block head_js -%}
// ... something
{%- endblock -%}
{%- endmacro %}
Я получаю следующее исключение
jinja2.exceptions.TemplateAssertionError: блок 'head_js' определен дважды
Мне кажется, что я столкнулся с проблемой дизайна, касающейся приоритета тегов block
над тегами macro
(т.е. макросы, похоже, не инкапсулируют блочные теги так, как я ожидаю).
Я полагаю, что мои вопросы довольно просты:
Может ли 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],
)
Это упрощает добавление 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 %}
, {% js_content %}
вызывается перед оператором include {% js %}
(т. е. x.html
полностью анализируется до запуска y.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 -->"
После завершения среда будет содержать переменную jbc
, в которой есть весь Javascript. Я могу вставить это через, например, string.Template
.
<script>
. - person Brian M. Hunt   schedule 27.11.2010extend
вместоinclude
, вы могли бы это сделать. Но из-за полного разделения между шагамиparse
иrender
вы не сможете изменить контекст родительской области, пока не станет слишком поздно. Кроме того, контекст Jinja должен быть неизменным. - person Wolph   schedule 30.11.2010Flask
я используюg
для хранения пропущенного кода. - person Brian M. Hunt   schedule 27.11.2011