For a long time now, I have been trying to figure out how to work with lists created on the fly in Django templates, meaning being able to:
- create a list directly in a django template,
- add new elements to that list,
- concatenate 2 lists together.
Those lists should be able to handle django objects and not only simple strings or so. For instance, in my case, I wanted my lists to be able to store form fields (example to follow).
After many researches, I figured out that it was impossible to do that but with simple things, and that I had to create my own custom tags if I ever wanted to achieve my purpose. My custom tag is written below. Please notice that this post helped me to do so.
The issue I am facing with
The custom tag works, and I use it in a for loop. The list generated here is correctly evolving according to the loop, and I can call it like any variable while still being in the loop (because it was exported in Django context): {{ listName }}
But! Once I'm outside that loop, my list does not seem having being updated at all! Like if it was only existing inside the for loop... I thought at first that when something was defined into a Django template context, it was available anywhere inside the template, and not only inside the block where it was defined. Am I missing something? Is that the normal behaviour for Django? I have not been able to find the answer to that question.
Custom tag
@register.tag()
def setList(parser, token):
"""
Use : {% setList par1 par2 ... parN as listName %}
'par' can be a simple variable or a list
To set an empty list: {% setList '' as listName %}
"""
data = list(token.split_contents())
if len(data) >= 4 and data[-2] == "as":
listName = data[-1]
items = data[1:-2]
return SetListNode(items, listName)
else:
raise template.TemplateSyntaxError(
"Erreur ! L'utilisation de %r est la suivante : {%% setList par1 par2 ... parN as listName %%}" % data[0]
)
class SetListNode(template.Node):
def __init__(self, items, listName):
self.items = []
for item in items: self.items.append(template.Variable(item))
self.listName = listName
def render(self, context):
finalList = []
for item in self.items:
itemR = item.resolve(context)
if isinstance(itemR, list): finalList.extend(itemR)
elif itemR == '': pass
else: finalList.append(itemR)
context[self.listName] = list(finalList)
return "" # django doc : render() always returns a string
Use of my custom tag in a Django Template
{% setList '' as new_list %}
new_list value is: {{ new_list }} # shows me an empty list: OK!
# then I iter on a forms.RadioSelect field
{% for field in form.fields %}
{% if field.choice_label in some_other_list %}
{% setList new_list field as new_list %}
{% endif %}
{{ new_list }} # a new item is added to new_list when necessary: OK!
{% endfor %}
{{ new_list }} # just shows an empty list, the one from the begining: THE ISSUE!
So: it looks like that my initial list is just being updated locally in my for loop. What a disappointment! Any idea about how I could use my custom list outside the loop? Is it impossible?
Thank you so much for the time you will take to help me with that thing. First time I am posting something in here, so if one needs anything please tell me!