4
votes

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 result: 4

Is there any way I can do this using the standard Django template tags? If not, what are my options?

4
@jMyles Thanks. Your post is quite old but it helped me to solve my pb. Adding "divisibleby:" in the template allows to perform some specific action when number of loops reaches a given number (12 in example below) {% for basket in baskets %} {% for egg in basket.eggs.all %} {# if total number of egg is a multiple of 12, it means that a new dozen has been found #} {% if forloop.counter|add:forloop.parentloop.counter|divisibleby:12 %} Dozen of eggs ! {% endif %} {% endfor %} {% endfor %}06userit

4 Answers

2
votes

Write a count template tag which will accumulate in a context variable.

{% for ... %}
  {% for ... %}
    {% count totalloops %}
  {% endfor %}
{% endfor %}
{{ totalloops }}
2
votes

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.

1
votes

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.

0
votes

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 %}