_Die eigentliche Vorlage, bei der ich zuerst auf das Problem gestoßen bin, war eher bausteinartig, aber ich beschreibe das Problem auf intuitivere Weise._
Angenommen, wir haben die folgende Vorlage:
{%- macro _tag(tag, id=none, classes=(), attrs={}) -%}
<{{ tag }}
{% if id %} id="{{ id }}" {% endif %}
{% if classes %} class="{{ classes|join(' ') }}" {% endif %}
{{ attrs|xmlattr }}>
{% if caller %} {{ caller() }} {% endif %}
</{{ tag }}>
{%- endmacro -%}
zusammen mit einer übergeordneten Vorlage, die versucht, {% call %}
es für einen bestimmten Fall zu tun:
{%- macro _div() -%}
{% call _tag('div', *varargs, **kwargs) %}
{% autoescape false %}
{% if caller %} {{ caller() }} {% endif %}
{% endautoescape %}
{% endcall %}
{%- endmacro -%}
Intuitiv sollte das Ausführen {% call _div(...) %} <b>foo</b> {% endcall %}
<b>foo</b>
an das _tag
-Makro übergeben, das es dann rendern würde (wörtlich, dank Autoescape). Aber das eigentliche Ergebnis ist UndefinedError: No caller defined
in der Zeile {{ caller() }}
innerhalb _div
. Es scheint, dass sich der Geltungsbereich von caller
nicht in den Block {% call %}
erstreckt, wodurch verkettete Aufrufe wie der oben abgebildete verhindert werden.
Es gibt eine hässliche Problemumgehung, die die neue Blockzuweisungsfunktion von 2.8 verwendet:
{%- macro _div() -%}
{% set content %}
{% if caller %} {{ caller() }} {% endif %}
{% endset %}
{% call _tag('div', *varargs, **kwargs) %}
{% autoescape false %} {{ content }} {% endautoescape %}
{% endcall %}
{%- endmacro -%}
aber natürlich wäre die Originalversion viel besser.
Ich kann dies mit diesem kleinen Beispiel reproduzieren:
{% macro a() %}
start of a
{% call b() %}
{{ caller() }}
{% endcall %}
end of a
{% endmacro %}
{% macro b() %}
start of b
{{ caller() }}
end of b
{% endmacro %}
{% call b() %}
inside b only
{% endcall %}
{#
{% call a() %}
inside a
{% endcall %}
#}
Dies funktioniert so wie es ist gut, wenn der letzte Block unkommentiert ist, erhalten wir die folgende Rückverfolgung:
Traceback (most recent call last):
...
File "/usr/lib/python2.7/dist-packages/jinja2/environment.py", line 894, in render
return self.environment.handle_exception(exc_info, True)
File "templates/test.j2", line 19, in top-level template code
{% call a() %}
File "templates/test.j2", line 3, in template
{% call b() %}
File "templates/test.j2", line 11, in template
{{ caller() }}
File "templates/test.j2", line 4, in template
{{ caller() }}
jinja2.exceptions.UndefinedError: No caller defined
Kleiner Hinweis zum Workaround:
Das neue Feature von 2.8 wird nicht benötigt, da dies auch gut funktioniert:
{% macro a() %}
start of a
{% set content=caller() %}
{% call b() %}
{{ content }}
{% endcall %}
end of a
{% endmacro %}
{% macro b() %}
start of b
{{ caller() }}
end of b
{% endmacro %}
{% call b() %}
inside b only
{% endcall %}
{% call a() %}
inside a
{% endcall %}
Dies ist beabsichtigt, da es immer auf das nächstgelegene Makro abzielt. Schließen als wontfix.
FYI, {% set caller_ = caller %}
funktioniert auch gut, dann kann es aufgerufen werden, wann immer es benötigt wird.
Auf jeden Fall wäre dies einen FAQ-Eintrag wert. Ich erinnere mich, dass ein Kollege vor einiger Zeit genau das gleiche Problem hatte, bis wir herausfanden, dass {% set caller_ = caller %}
vor dem Wechsel in eine andere Anrufsperre hilft.
Ja, wir sollten das vielleicht in die Dokumentation aufnehmen.
Hilfreichster Kommentar
Kleiner Hinweis zum Workaround:
Das neue Feature von 2.8 wird nicht benötigt, da dies auch gut funktioniert: