Jinja: L'utilisation de {{ caller() }} dans {% call %} échoue

Créé le 23 sept. 2014  ·  5Commentaires  ·  Source: pallets/jinja

_Le modèle réel où j'ai rencontré le problème en premier était plus passe-partout, mais celui-ci décrit le problème de manière plus intuitive._

Supposons que nous ayons le modèle suivant :

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

avec un modèle de niveau supérieur qui essaie de le {% call %} pour un cas spécifique :

{%- macro _div() -%}
  {% call _tag('div', *varargs, **kwargs) %}
    {% autoescape false %}
      {% if caller %} {{ caller() }} {% endif %}
    {% endautoescape %}
  {% endcall %}
{%- endmacro -%}

Intuitivement, faire {% call _div(...) %} <b>foo</b> {% endcall %} devrait passer <b>foo</b> à la macro _tag , qui la restituerait alors (textuellement, grâce à autoescape). Mais le résultat réel est UndefinedError: No caller defined sur la ligne {{ caller() }} à l'intérieur _div . Il semblerait que la portée de caller ne s'étende pas au bloc {% call %} , empêchant ainsi les appels enchaînés comme celui décrit ci-dessus.

Il existe une solution de contournement laide qui utilise la nouvelle fonctionnalité d'affectation de bloc de 2.8 :

{%- macro _div() -%}
  {% set content %}
    {% if caller %} {{ caller() }} {% endif %}
  {% endset %}
  {% call _tag('div', *varargs, **kwargs) %}
    {% autoescape false %} {{ content }} {% endautoescape %}
  {% endcall %}
{%- endmacro -%}

mais, bien sûr, la version originale serait bien plus préférable.

Commentaire le plus utile

Petite note sur la solution de contournement:

La nouvelle fonctionnalité de 2.8 n'est pas nécessaire car elle fonctionne également correctement :

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

Tous les 5 commentaires

Je suis capable de reproduire cela avec ce petit exemple:

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

Cela fonctionne bien tel quel, lorsque le dernier bloc n'est pas commenté, nous obtenons la trace suivante :

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

Petite note sur la solution de contournement:

La nouvelle fonctionnalité de 2.8 n'est pas nécessaire car elle fonctionne également correctement :

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

C'est en quelque sorte intentionnel car il s'étend toujours à la macro la plus proche. Fermeture comme coutume.

Pour votre information, {% set caller_ = caller %} fonctionne également très bien, alors il peut être appelé chaque fois que nécessaire.

Quoi qu'il en soit, cela pourrait valoir une entrée FAQ. Je me souviens qu'un collègue avait exactement le même problème il y a quelque temps jusqu'à ce que nous découvrions que {% set caller_ = caller %} avant d'entrer dans un autre bloc d'appels aide.

Ouais, on pourrait vouloir mettre ça dans les docs.

Cette page vous a été utile?
0 / 5 - 0 notes