75

In my views.py, I'm building a list of two-tuples, where the second item in the tuple is another list, like this:

[ Product_Type_1, [ product_1, product_2 ],
  Product_Type_2, [ product_3, product_4 ]]

In plain old Python, I could iteration the list like this:

for product_type, products in list:
    print product_type
    for product in products:
        print product

I can't seem to do the same thing in my Django template:

{% for product_type, products in product_list %}
    print product_type
    {% for product in products %}
        print product
    {% endfor %}
{% endfor %}

I get this error from Django:

Caught an exception while rendering: zip argument #2 must support iteration

Of course, there is some HTML markup in the template, not print statements. Is tuple unpacking not supported in the Django template language? Or am I going about this the wrong way? All I am trying to do is display a simple hierarchy of objects - there are several product types, each with several products (in models.py, Product has a foreign key to Product_type, a simple one-to-many relationship).

Obviously, I am quite new to Django, so any input would be appreciated.

Chris Lawlor
  • 47,306
  • 11
  • 48
  • 68
  • 2
    You talk about tuples, but your question contains only lists. They are different things in python. – Harley Holcombe Nov 07 '08 at 02:48
  • You actually don't have two-tuples. Look carefully at your list, you have 4 list items. As Jonny Buchanan noted, you need to do this: [ [Product_Type_1, [ product_1, product_2 ], ], [Product_Type_2, [ product_3, product_4 ], ], ] to get a list version of two-tuples. – MontyThreeCard Mar 17 '17 at 14:03

5 Answers5

102

Another way is as follows.

If one has a list of tuples say:

mylst = [(a, b, c), (x, y, z), (l, m, n)]

then one can unpack this list in the template file in the following manner. In my case I had a list of tuples which contained the URL, title, and summary of a document.

{% for item in mylst %}    
     {{ item.0 }} {{ item.1}} {{ item.2 }}    
{% endfor %}
Dimitrios Mistriotis
  • 2,626
  • 3
  • 28
  • 45
Ashwin Rao
  • 1,021
  • 1
  • 7
  • 2
76

it would be best if you construct your data like {note the '(' and ')' can be exchanged for '[' and ']' repectively, one being for tuples, one for lists}

[ (Product_Type_1, ( product_1, product_2 )),
   (Product_Type_2, ( product_3, product_4 )) ]

and have the template do this:

{% for product_type, products in product_type_list %}
    {{ product_type }}
    {% for product in products %}
        {{ product }}
    {% endfor %}
{% endfor %}

the way tuples/lists are unpacked in for loops is based on the item returned by the list iterator. each iteration only one item was returned. the first time around the loop, Product_Type_1, the second your list of products...

Jake
  • 3,427
  • 2
  • 28
  • 23
  • 1
    i.e. you thought you were building a list of two-tuples, but you forgot the tuple part - note the extra parentheses surrounding the product type/product list pairs in Cipher's answer – Jonny Buchanan Nov 07 '08 at 20:17
  • This does not work for older Django versions (like the one used in the GAE). Use Ashwin Rao's answer if you get this error: " 'for' statements with five words should end in 'reversed' " – Franziskus Karsunke Aug 01 '11 at 12:14
10

You must used this way:

{% for product_type, products in product_list.items %}
    {{ product_type }}
    {% for product in products %}
       {{ product }}
    {% endfor %}
{% endfor %}

Don't forget the variable items in the dictionary data

Peter
  • 6,509
  • 4
  • 30
  • 34
3

If you have a fixed number in your tuples, you could just use indexing. I needed to mix a dictionary and the values were tuples, so I did this:

In the view:

my_dict = {'parrot': ('dead', 'stone'), 'lumberjack': ('sleep_all_night', 'work_all_day')}

In the template:

<select>
  {% for key, tuple in my_dict.items %}
    <option value="{{ key }}" important-attr="{{ tuple.0 }}">{{ tuple.1 }}</option>
  {% endfor %}
</select>
famousfilm
  • 31
  • 3
2

Just send the template a list of product types and do something like:

{% for product_type in product_type_list %}
    {{ product_type }}
    {% for product in product_type.products.all %}
        {{ product }}
    {% endfor %}
{% endfor %}

It's been a little while so I can't remember exactly what the syntax is, let me know if that works. Check the documentation.

Harley Holcombe
  • 175,848
  • 15
  • 70
  • 63
  • Chris Lawlor hasn't explained how he's ended up with the data structure he has (which isn't actually list of two-tuples), but this suggested approach would result in N + 1 queries to display the product list. – Jonny Buchanan Nov 07 '08 at 20:14
  • Here be dragons. Using this suggestion, you're making a separate trip to the database for each product_type. I suspect that minimizing the database load is probably the motivation behind using tuples in the first place. – btubbs Dec 03 '09 at 01:05
  • I think you could use select_related() to mitigate that particular concern, right? – Charles Offenbacher Apr 16 '11 at 00:58