如何确保Jinja自定义标签只输出一次?

4 投票
5 回答
4137 浏览
提问于 2025-04-16 00:41

我在Jinja2中有一个自定义标签,我希望它在第一次调用时输出一些内容。假设我有以下模板:

1. {% only_once %}
2. {% only_once %}
3. {% only_once %}

我希望输出结果是:

1. "I only get printed once!"
2.
3.

我猜最好的办法是在模板的上下文中设置一个标志,来跟踪我是否已经打印过某些内容。这里有一段代码示例,但这样做对吗?

class OnlyOnceExtension(Extension):
    tags = set(['only_once'])

    @contextfunction
    def parse(self, context, parser):
        if hasattr(context, 'my_flag') and context.my_flag:
            return Output("")
        else:
            return Output("I only get printed once!")

这样做正确吗?我看到有些内容提到上下文是不可变的,这样的话会不会不行?(可以查看http://jinja.pocoo.org/2/documentation/api,搜索一下不可变的内容)

5 个回答

1

我会先定义一个布尔值和一个宏,然后再继续。与其直接打印变量,不如打印宏的结果。这个宏里面用到了一个if语句和那个布尔值,来决定是否要打印。所以,你可以得到如下的代码:

{{ set was_printed = false }}
{% macro print_once(to_print) %}
    {% if was_printed is sameas false %}
        {{ to_print }}
        {% set was_printed = true %}
    {% endif %}
{% endmacro %}
1. {% print_once(only_once) %}
2. {% print_once(only_once) %}
3. {% print_once(only_once) %}
13

如果你想完全用Jinja来实现这个功能,你可以这样检查循环中的index变量。

{% for bar in bars %}
    {% if loop.index == 1 %}
        Print me once
    {% endif %}
    Print me every time
{% endfor %}
4

我的建议是用Python代码来实现这个:

class OnlyOnce(object):
    def __init__(self, data):
        self.data = data
        self.printed = False

    def __str__(self):
        if self.printed is False:
            self.printed = True
            return self.data
        return ''

在你的Python代码中创建一个OnlyOnce实例,然后把它传递给模板。每次你想用的时候,只需使用{{ only_once }}就可以了。

我注意到很多使用Jinja的人都想以Django的方式来做事情,比如写扩展。但其实Jinja的表达式、导入等功能已经很强大了,你不需要为了每件事都去写扩展。

还有,使用context.my_flag这种方式是个坏主意。只有模板才能修改上下文。绝对不可以。

撰写回答