Django template counter in nested loops

微笑、不失礼 提交于 2019-12-04 17:40:45

问题


Hi I have a list of two dictionaries I am passing to a Django template:

base_parts = [
    {'important item': 43},
    {'lesser item': 22, 'lesser item': 3, 'lesser item': 45}
]

in my template I can do this:

{% for base_part in base_parts %}
    {% for k, v in base_part.items %}

    {# ...do stuff #}

    {# I try to get a running total of items to use as an ID #}
    inner ID: {% forloop.counter0 %}< br/>
    outer ID: {% forloop.parentloop.counter0 %}< br/>

    {% endfor %}
{% endfor %}

As you can see, what I want is a running total of the total number of items I have iterated through, but both methods I have included return duplicates. I know I could concatenate the loops, but I am using a formset and really would like the ids to be indexed 0,1,2...etc.

Is there a way to achieve this type of count in the template?

Any help much appreciated.

EDIT

output at the moment looks like:

outerID: 0<br />
innerID: 0<br />
outerID: 0<br />
innerID: 1<br />
outerID: 1<br />
innerID: 0<br />
outerID: 1<br />
innerID: 1<br />
outerID: 1<br />
innerID: 2<br />

I want:

totalID: 0<br />
totalID: 1<br />
totalID: 2<br />
totalID: 3<br />
totalID: 4<br />
totalID: 5<br />
totalID: 6<br />
totalID: 7<br />
totalID: 8<br />
totalID: 9<br />

回答1:


I found a better solution with itertools. (Better than my previous answer) You can set current state of the loop to the itertools variable sent to the view context. This time i tried on a dummy Django project and it works like a charm.

views.py:

from django.shortcuts import render_to_response
import itertools

def home(request):
    iterator=itertools.count()
    base_parts = [
        {'important item': 43},
        {'lesser item1': 22, 'lesser item2': 3, 'lesser item3': 45},
        {'most important item': 55}
    ]
    return render_to_response('index.html', 
                             {'base_parts': base_parts, 'iterator':iterator})

index.html:

{% for base_part in base_parts %}
    {% for k, v in base_part.items %}
        {{ iterator.next }} - {{ v }}<br/>
    {% endfor %}
{% endfor %}

HTML Output:

0 - 43
1 - 22
2 - 45
3 - 3
4 - 55

Sorted values:

(This part is not an answer to the actual question. It's more like I'm playing around)

You can use Django's SortedDict instead of Python's built-in dictionary to keep items order.

views.py

from django.shortcuts import render_to_response
import itertools
from django.utils.datastructures import SortedDict

def home(request):
    iterator=itertools.count()
    base_parts = [
        SortedDict([('important item', 43)]),
        SortedDict([('lesser item1', 22), 
                    ('lesser item2', 3), 
                    ('lesser item3', 45)]),
        SortedDict([('most important item', 55)])
    ]
    print base_parts[1]
    return render_to_response('index.html', 
                             {'base_parts': base_parts, 'iterator':iterator})

HTML Output:

0 - 43
1 - 22
2 - 3
3 - 45
4 - 55

Edit 2014-May-25

You can also use collections.OrderedDict instead of Django's SortedDict.

Edit 2016-June-28

Calling iterator.next doesn't work in Python 3. You can create your own iterator class, inheriting from itertools.count:

import itertools
class TemplateIterator(itertools.count):
    def next(self):
        return next(self)



回答2:


In an ideal world, you should avoid putting this kind of logic in the template. If you are not preserving the hierarchy in your output (eg displaying these items as a list of lists) flatten the list and use a simple for loop and the loop counter.

However, the ideal solution isn't always an option. In theory, I believe the following could/should work

{% for base_part in base_parts %}     
    {% with outerCount = forloop.parentloop.counter0 %}
    {% for k, v in base_part.items %}
        ...
        {% with innerCounter = forloop.counter %}
        {{ outerCounter|add:innerCounter }}
    {% endfor %}
{% endfor %}



回答3:


UPDATE: This is not a correct answer. I am just keeping it here to display what doesn't work.

I have to admit that i haven't tried this one but you can use with and add statements.

{% with total=0 %}
    {% for base_part in base_parts %}
        {% for k, v in base_part.items %}

        {# ...do stuff #}

        {# I try to get a running total of items to use as an ID #}
        totalId: {{ total|add:"1" }} <br/>
        {% endfor %}
    {% endfor %}
{% endwith %}

This would probably work on template level but i think a better approach is calculating it on the view level and passing a dictionary to the template which includes calculated values.




回答4:


It would be quite and efficient to do it at the view code...

    #Calculate the count
    count = 0
    for item in base_parts:
       count+=len(item.keys())

    run_range = range(count)

    return to the template

    #template
    {% for x in run_range|slice:'1' %}
    {{x}}
    {% endfor %}



回答5:


So I actually just appended the dictionary items to a list, ensuring that one I wanted was first:

base_parts = []
for k, v in rails_dic.items():
    base_parts.append({k: v})
for k, v in accessories_dic.items():
    base_parts.append({k: v})

This may not be the best way to do this, and I am open to a more pythonic way as the correct answer.

I think that taking care of this in the template is both tricky and not appropriate. This is why I my not accepting those answers. I would like that future visitors can see that the solution was sought for the overall problem in the more correct way, not just how to hack it in the templates.



来源:https://stackoverflow.com/questions/13870890/django-template-counter-in-nested-loops

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!