Django template: get total iteration count for nested loops
I have two nested for loops inside a template. I need to get the total iterations made since the parent for loop started. The counter needs to be incremented only when the child for iterates.
For example:
Each loop goes from 1 to 3 (included)
Parent loop - 1st iteration
Child loop - 3rd iteration
Wanted result: 3
Parent loop - 2nd iteration
Child loop - 1st iteration
Wanted re开发者_C百科sult: 4
Is there any way I can do this using the standard Django template tags? If not, what are my options?
Write a count
template tag which will accumulate in a context variable.
{% for ... %}
{% for ... %}
{% count totalloops %}
{% endfor %}
{% endfor %}
{{ totalloops }}
Either you can use {{forloop.counter |add: forloop.parentcounter.counter }} but depend on the situation if you want to reset the counter then you need to write your own custom python method and later you can call it from django template.
Like in your views add-
class make_incrementor(object):
count = 0
def __init__(self, start):
self.count = start
def inc(self, jump=1):
self.count += jump
return self.count
def res(self):
self.count = 0
return self.count
def EditSchemeDefinition(request, scheme_id):
iterator_subtopic = make_incrementor(0)
scheme_recs = scheme.objects.get(id=scheme_id)
view_val = {
'iterator_subtopic': iterator_subtopic,
"scheme_recs": scheme_recs,
}
return render(request, "edit.html", view_val)
Later in your django template we can call "iterator_subtopic" methods to increment or reset its value like:-
<td id="subTopic" class="subTopic">
<p hidden value="{{ iterator_subtopic.res }}"></p>
{% for strand in scheme_recs.stand_ids.all %}
{{ iterator_subtopic.res }}
{% for sub_strand in strand.sub_strand_ids.all %}
{% for topic in sub_strand.topic_ids.all %}
{% for subtopic in topic.sub_topic_ids.all %}
<input id="subTopic{{ iterator_subtopic.inc }}" class="len"
value="{{ subtopic.name }}">
<br>
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
So It will keep incrementing the value and also we can reset it where we want.
With class-based views (specifically using Python 3 and Django 2.1), and using @Javed's answer as a starting point, in the view you can do:
class Accumulator:
def __init__(self, start=0):
self.reset(start)
def increment(self, step=1):
step = 1 if not isinstance(step, int) else step
self.count += step
return self.count
def reset(self, start=0):
start = 0 if not isinstance(start, int) else start
self.count = start
return self.count
class MyView(ParentView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['counter'] = Accumulator() # use start=-1 for a zero-based index.
return context
then in the template, you can do:
{% with counter.increment as count %}
<input id="form-input-{{ count }}">
{% endwith %}
Do you know, going in, how many loops there will be?
If so, an easy way is:
{{ forloop.counter |add: forloop.parentcounter.counter }}
etc.
It's a bit stinky vis a vis logic separation (@Ignacio's suggestion is better on this front for sure), but I think it's acceptable if it's kept neat and orderly.
精彩评论