Django Do Once Custom Template Tag
I'm trying to create a custom template tag that only renders a block of code once, regardless of how many times the tag/partial that contains it is executed.
This is how I've implemented it, but as you can see, it's a bit hackish:
my_partial.html:
{% on开发者_如何学Goce 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)
Specifically, I'm using the request.GET dictionary as a mutable global scope. It's hackish and obviously not what the request object is designed to do, but it works.
Ideally, I'd like to use something like the context, but I found that it isn't shared between calls to this tag. i.e. self.var_name in context
is always False, rendering it useless as a global scope.
Why isn't context shared the same way request is shared? Is there someway to make it truly shared, or is there some other object I can use to store globally accessible variables within a request?
I'm not exactly sure what you need to accomplish or if your approach is indeed the best approach, but I'd suggest your look into the forloop.first
variable before you go too far down this road. Your approach seems awkward at best at a glance, but I could be wrong since I don't know the specifics of the situation
django for template tag
Most likely you should be able to make this work to your needs, however if it falls short I'd suggest that the source for the for
template tag (and it's forloop
variable) would likely be very illustrative on how you might implement what you're looking to do.
What I ended up doing is saving a variable within the context and checking for that in the Node's render method:
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__)
...
精彩评论