Django 一次性自定义模板标签
我正在尝试创建一个自定义模板标签,这个标签只会渲染一段代码一次,无论包含它的标签或部分被执行多少次。
这是我实现的方式,但如你所见,有点不太优雅:
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
这个变量。乍一看,你的方法似乎有点别扭,不过我可能错了,因为我不知道具体情况。
你很可能可以让这个方法满足你的需求,不过如果不行的话,我建议你查看一下 for
模板标签的源代码(还有它的 forloop
变量),这可能会对你实现想要的功能有很大帮助。