Jinja: Использование {{ caller() }} внутри {% call %} завершается ошибкой

Созданный на 23 сент. 2014  ·  5Комментарии  ·  Источник: pallets/jinja

_Фактический шаблон, в котором я впервые столкнулся с проблемой, был более шаблонным, но этот описывает проблему более интуитивно понятным образом._

Предположим, у нас есть следующий шаблон:

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

вместе с шаблоном более высокого уровня, который пытается {% call %} для некоторого конкретного случая:

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

Интуитивно, выполнение {% call _div(...) %} <b>foo</b> {% endcall %} должно передать <b>foo</b> _tag , который затем отобразит его (дословно, благодаря автоэкранированию). Но фактический результат UndefinedError: No caller defined в строке {{ caller() }} внутри _div . Казалось бы, область действия caller не распространяется на блок {% call %} , тем самым предотвращая связанные вызовы, подобные изображенному выше.

Существует уродливый обходной путь, который использует новую функцию назначения блоков из 2.8:

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

но, конечно, первоначальный вариант был бы гораздо предпочтительнее.

Самый полезный комментарий

Небольшое примечание о обходном пути:

Новая функция 2.8 не нужна, так как она тоже работает нормально:

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

Все 5 Комментарий

Я могу воспроизвести это с помощью этого небольшого примера:

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

Это отлично работает как есть, когда последний блок раскомментирован, мы получаем следующую трассировку:

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

Небольшое примечание о обходном пути:

Новая функция 2.8 не нужна, так как она тоже работает нормально:

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

Это сделано намеренно, так как всегда ограничивается ближайшим макросом. Закрытие как wontfix.

К вашему сведению, {% set caller_ = caller %} также работает нормально, тогда его можно вызывать всякий раз, когда это необходимо.

В любом случае, это может стоить записи в FAQ. Я помню, как некоторое время назад у коллеги была точно такая же проблема, пока мы не выяснили, что {% set caller_ = caller %} перед переходом в другой блок вызовов помогает.

Да, возможно, мы захотим внести это в документы.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги