Jinja: 在 {% call %} 中使用 {{ caller() }} 失败

创建于 2014-09-23  ·  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宏,然后它会渲染它(逐字逐句,感谢 autoescape)。 但实际结果是UndefinedError: No caller defined内的{{ caller() }}行上的_divcaller的范围似乎没有延伸到{% 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 %}也可以正常工作,然后可以在需要时调用它。

无论如何,这可能值得一个常见问题解答条目。 我记得前段时间一位同事遇到了完全相同的问题,直到我们在进入另一个调用块之前发现{% set caller_ = caller %}有帮助。

是的,我们可能想把它放到文档中。

此页面是否有帮助?
0 / 5 - 0 等级