В 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 %}
.
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 %}
Ожидаемый результат
Когда я запускаю 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>
Фактический результат
Фактические результаты не внушают оптимизма. Я получаю несколько типов потенциально освещающих ошибок, например ::1010
TypeError: макрос 'js' не принимает аргумента ключевого слова 'caller'
или, когда я пытаюсь добавить другой базовый макрос, такой как
{% macro js2() -%}
{%- block head_js -%}
// ... something
{%- endblock -%}
{%- endmacro %}
я получаю следующее исключение
jinja2.exceptions.TemplateAssertionError: блок 'head_js', определенный дважды
Мне кажется, что я сталкиваюсь с проблемой проектирования, касающейся приоритета тегов block
над тегами macro
(т.е. макросы, похоже, не инкапсулируют блочные теги так, как я ожидаю).
Полагаю, мои вопросы довольно просты:
Может ли Jinja2 делать то, что я пытаюсь? Если да, то как?
Если нет, есть ли другой шаблонизатор на основе Python, который поддерживает этот тип паттернов (например, мако, гэнши и т. Д.), Который без проблем работал бы в 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 %}
, {% js_content %}
вызывается перед оператором {% js %}
include (т.е. 1080 * полностью анализируется перед запуском 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
.