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

时间:2010-07-01 15:16:46

标签: python jinja2

我在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并搜索不可变的)

4 个答案:

答案 0 :(得分:5)

如果你想纯粹使用Jinja,你可以用这种方式检查loop.index变量,

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

答案 1 :(得分: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-ish的方式做事,即编写扩展。但Jinja的表达式/导入/任何功能足够强大,您不必为所有内容使用扩展。

是的,使用context.my_flag这个东西是个坏主意。只有模板才能修改上下文。的 EVER。

答案 2 :(得分: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) %}

答案 3 :(得分:0)

Jinja并不总是重新评估宏,因此设置宏变量不会阻止两次创建值。公认的解决方案有效,但是需要您在烧瓶视图级别上对对象进行硬编码。 我通过创建一个自定义的Jinja过滤器解决了这个问题,该过滤器每次请求仅返回一次值。

过滤器:

from flask import g    

def onetime(val, atr):
    """
    this filter will return the given value only once per request

    :param val: the value to return 1 time
    :type val: str
    :param atr: the attribute to remember this value by
    :type atr: str
    :return: returns True for this attribute once per request
    :rtype: str
    """
    # has this attribute been set for this request?
    if getattr(g, atr, False):
        # the value has already been returned for this request
        return ""
    # set the flag
    setattr(g, atr, True)
    # return the value, this is the first call
    return val

将过滤器添加到烧瓶应用

app.jinja_env.filters['onetime'] = onetime

创建一个Jinja宏,其中包含您只想输出一次的文本: macros.html

{% macro my_macro() %}
This text should only be created once
{% endmacro %}

并使用它:

{{ my_macro() | onetime('a_name') }}
{{ my_macro() | onetime('a_name') }}
{{ my_macro() | onetime('a_name') }}