Django 一次性自定义模板标签

1 投票
2 回答
1062 浏览
提问于 2025-04-16 21:32

我正在尝试创建一个自定义模板标签,这个标签只会渲染一段代码一次,无论包含它的标签或部分被执行多少次。

这是我实现的方式,但如你所见,有点不太优雅:

my_partial.html:

{% once mycontent %}
    this will only show once
{% endonce%}

my_template.html:

{% load my_tags %}
{% for i in list %}
    {% my_partial %}
{% endfor %}

my_tags.py:

@register.inclusion_tag('my_partial.html',takes_context=True)
def my_partial(context):
    return dict(arbitrary extra data)

@register.tag(name="once")
def do_once(parser, token):
    try:
        # Splitting by None == splitting by spaces.
        tag_name, var_name = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires arguments" % token.contents.split()[0])
    nodelist = parser.parse(('endonce',))
    parser.delete_first_token()
    return DoOnceNode(nodelist, var_name)

class DoOnceNode(template.Node):
    def __init__(self, nodelist, var_name):
        self.nodelist = nodelist
        self.var_name = '_do_once_'+var_name
    def render(self, context):
        request = context['request']

        # Make request.GET mutable.
        request.GET = dict(request.GET)

        if self.var_name in request.GET:
            return ''
        else:
            request.GET[self.var_name] = 1
            return self.nodelist.render(context)

具体来说,我使用了 request.GET 字典作为一个可以改变的全局范围。这种做法有点不太正规,显然也不是 request 对象的设计初衷,但它确实有效。

理想情况下,我想使用类似上下文的东西,但我发现它在调用这个标签时并不会共享。也就是说,self.var_name in context 总是返回 False,这让它作为全局范围变得没用。

为什么上下文没有像请求那样共享呢?有没有办法让它真正共享,或者有没有其他对象可以用来在请求中存储全局可访问的变量呢?

2 个回答

1

我最后做的就是在上下文中保存一个变量,然后在节点的渲染方法里检查这个变量:

class CustomNode(template.Node):
    def render(self, context: dict) -> str:
        context['already_rendered'] = context.get('already_rendered', set())

        if self.__class__ in context['already_rendered']:
            return ''

        context['already_rendered'].add(self.__class__)

        ...
1

我不太确定你想要实现什么,或者你的方法是否真的是最好的,但我建议你在继续之前先看看 forloop.first 这个变量。乍一看,你的方法似乎有点别扭,不过我可能错了,因为我不知道具体情况。

django 的 for 模板标签

你很可能可以让这个方法满足你的需求,不过如果不行的话,我建议你查看一下 for 模板标签的源代码(还有它的 forloop 变量),这可能会对你实现想要的功能有很大帮助。

撰写回答