Django Template For Loop

The {% for %} tag iterates over a list, queryset, or dictionary in a template, with forloop giving you loop metadata like the current index.

The {% for %} Tag

{% for %} iterates over any list, tuple, queryset, or other iterable found in the context, running the enclosed block once per item and binding a loop variable of your choosing to the current item. Like if, it must be explicitly closed with {% endfor %}.

Example

<ul>
{% for article in articles %}
  <li>{{ article.title }}</li>
{% endfor %}
</ul>

Loop Metadata with forloop

Inside every for loop, Django provides a forloop variable with metadata about the current iteration: forloop.counter (1-indexed position), forloop.counter0 (0-indexed), forloop.first and forloop.last (booleans), forloop.revcounter (position counting down to 1), and forloop.parentloop (the enclosing loop's forloop, for nested loops).

Example

<table>
{% for article in articles %}
  <tr class="{% if forloop.last %}last-row{% endif %}">
    <td>{{ forloop.counter }}</td>
    <td>{{ article.title }}</td>
  </tr>
{% endfor %}
</table>

Handling Empty Lists and Nested Loops

The optional {% empty %} clause renders in place of the loop body when the iterable has zero items, saving you a separate {% if articles %} check. Inside a nested loop, forloop.parentloop gives you access to the outer loop's counter, since the inner forloop only tracks the inner iteration.

Example

{% for category in categories %}
  <h2>{{ category.name }}</h2>
  <ol>
  {% for product in category.products.all %}
    <li>{{ forloop.parentloop.counter }}.{{ forloop.counter }} {{ product.name }}</li>
  {% empty %}
    <li>No products in this category yet.</li>
  {% endfor %}
  </ol>
{% empty %}
  <p>No categories to show.</p>
{% endfor %}
  • forloop.counter — current iteration, starting at 1
  • forloop.counter0 — current iteration, starting at 0
  • forloop.first / forloop.last — True on the first or last iteration
  • forloop.revcounter — iterations remaining, counting down to 1
  • forloop.parentloop — the enclosing loop's forloop, for nested loops
AttributeExample Output (3-item list)
forloop.counter1, 2, 3
forloop.counter00, 1, 2
forloop.lastFalse, False, True
forloop.revcounter3, 2, 1
Note: {% empty %} only fires when the iterable is empty, not when a variable is missing from the context entirely — an undefined variable is treated as an empty list by {% for %}, so both cases end up rendering the empty block.

Exercise: Django Template For Loop

Which tag correctly ends a for loop in a Django template?