问题
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