Jinja: Die Verwendung von {{ caller() }} innerhalb von {% call %} schlägt fehl

Erstellt am 23. Sept. 2014  ·  5Kommentare  ·  Quelle: pallets/jinja

_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.

Hilfreichster Kommentar

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 %}

Alle 5 Kommentare

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.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen