重新定义导入的jinja宏

时间:2014-10-27 07:22:28

标签: jinja2

我们说我有这个带宏的模板(这些是简化的):

{# macros.html #}
{% macro a(opts = {}) %}
   {{ opts.a }}
{% endmacro %}

{% macro b(opts = {}) %}
   {{ opts.b }}
{% endmacro %}

和包含覆盖

的这个
{# macros_override.html #}
{% macro a(opts = {}) %}
    Overridden: {{ opts.a }}
{% endmacro %}

然后我希望在同一名称空间macros

下有一个包含所有这些宏的模板
{# template.html #}
{% import 'macros.html' as macros %}
{% import 'macros_override.html' as macros %}

{{ macros.a({ 'a': 'foo' }) }}
{{ macros.b({ 'b': 'bar' }) }}

和我期望的输出:

Overridden: foo
bar

b未定义。我尝试将macros_override.html模板更改为:

{# macros_override.html #}
{% extends 'macros.html' %}
{% macro a(opts = {}) %}
    Overridden: {{ opts.a }}
{% endmacro %}

然后仅将重叠模板导入为宏,但在这种情况下宏a未被覆盖,我不知道原因。

我可以通过其他导入以某种方式覆盖jinja中的导入宏吗?

2 个答案:

答案 0 :(得分:6)

所以在我的同事的帮助下,我明白了。

  

在孩子之后评估父模板   http://jinja.pocoo.org/docs/dev/faq/#my-macros-are-overridden-by-something

这意味着如果你想使用第二个选项,你只需要在父模板中检查宏是否存在。

像这样工作:

{# macros.html #}
{% if not a %}
{% macro a(opts = {}) %}
   {{ opts.a }}
{% endmacro %}
{% endif %}

{% if not b %}
{% macro b(opts = {}) %}
   {{ opts.b }}
{% endmacro %}
{% endif %}

和包含覆盖

的这个
{# macros_override.html #}
{% extends 'macros.html' %}
{% if not a %}{# I repeat here in case there's gonna be a double extend #}
{% macro a(opts = {}) %}
    Overridden: {{ opts.a }}
{% endmacro %}
{% endif %}

然后就像这样导入它们

{# template.html #}
{% import 'macros_override.html' as macros %}

{{ macros.a({ 'a': 'foo' }) }}
{{ macros.b({ 'b': 'bar' }) }}

并按预期输出

Overridden: foo
bar

答案 1 :(得分:0)

我们遇到了类似的问题,我们希望用户能够通过扩展默认模板来覆盖单个宏。重复 {% if not a %} 在这里有点乏味,所以我们用 Jinja2 扩展修复了这个问题:

class DefaultMacroExtension(ext.Extension):
    """
    This extension provides a new `{% defaultmacro %}` statement, which defines a macro only if it does not exist.
    For example,

        {% defaultmacro example() %}
            test 123
        {% enddefaultmacro %}

    is equivalent to

        {% macro default_example() %}
        test 123
        {% endmacro %}
        {% if not example %}
            {% macro example() %}
                test 123
            {% endmacro %}
        {% endif %}

    The default implementation is also available as `default_$macroname`, which makes it possible
    to reference it in the override.
    """

    tags = {"defaultmacro"}

    def parse(self, parser):
        m = nodes.Macro(lineno=next(parser.stream).lineno)
        name = parser.parse_assign_target(name_only=True).name
        m.name = f"default_{name}"
        parser.parse_signature(m)
        m.body = parser.parse_statements(("name:enddefaultmacro",), drop_needle=True)

        if_stmt = nodes.If(
            nodes.Not(nodes.Name(name, "load")),
            [nodes.Macro(name, m.args, m.defaults, m.body)],
            [],
            [],
        )
        return [m, if_stmt]

env = Environment(
    extensions=[DefaultMacroExtension],
)