Submit Blog  RSS Feeds

Tuesday, January 29, 2013

The forloop django tempalte variable

Django template for-loop iteration is usually executed on QuerySets (paginated or not). For front-end purposes it's sometimes good to provide row numbers (especially if the data is supposed to be presented in a specific order).  A first non-django solution that comes my mind is something like this:


def test_view(request):
    ctx = {}
    objects = MyModel.objects.all()
    ctx['objects'] = zip(range(1,objects.count()+1), objects)
    return render_to_response("app/test.html", ctx)


And for the template:

<table>
{% for el in objects %}
    <tr>
        <td>el.0</td>
        <td>el.1.some_field</td>
        {% comment %} Other fields.... {% endcomment %}
   </tr>
{% endfor %}
</table>

This is a great python solution, but it's not a django solution. First of all, by using zip function we select the records from the database, adding pagination now would be a bit complicated (using the PaginationMiddleware won't be effecient since it works well only on lazy QuerySets). Another Disadvantage is the need of addressing the counter and data by index. Of course we could make the counter an attribute of the data objects... but it's still not THE solution.

Django provides a better way of solving this problem. Inside each for-loop you may access a forloop template variable. According to the django docs the following attributes are available:

forloop.counter The current iteration of the loop (1-indexed)
forloop.counter0 The current iteration of the loop (0-indexed)
forloop.revcounter The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)
forloop.first True if this is the first time through the loop
forloop.last True if this is the last time through the loop
forloop.parentloop For nested loops, this is the loop "above" the current one

Now the template could look like this:


<table>
{% for el in objects %}
    <tr>
        <td>{{forloop.counter}}</td>
        <td>{{el.some_field}}</td>
        {% comment %} Other fields.... {% endcomment %}
   </tr>
{% endfor %}
</table>

This is more elegant and practical. It's also easy to combine it with the PaginationMiddleware. Ale you need to do is add forloop.counter with each page start index.

Cheers!
KR

No comments:

Post a Comment

free counters