Django template can't loop defaultdict

user216171 picture user216171 · Jan 21, 2011 · Viewed 7.5k times · Source
import collections

data = [
  {'firstname': 'John', 'lastname': 'Smith'}, 
  {'firstname': 'Samantha', 'lastname': 'Smith'}, 
  {'firstname': 'shawn', 'lastname': 'Spencer'}, 
]

new_data = collections.defaultdict(list)

for d in data:
    new_data[d['lastname']].append(d['firstname'])

print new_data

Here's the output:

defaultdict(<type 'list'>, {'Smith': ['John', 'Samantha'], 'Spencer': ['shawn']})

and here's the template:

{% for lastname, firstname in data.items %}
  <h1> {{ lastname }} </h1>
  <p> {{ firstname|join:", " }} </p>
{% endfor %}

But the loop in my template doesn't work. Nothing shows up. It doesn't even give me an error. How can i fix this? It's supposed to show the lastname along with the firstname, something like this:

<h1> Smith </h1>
<p> John, Samantha </p>

<h1> Spencer </h1>
<p> shawn </p>

Answer

S&#233;bastien Trottier picture Sébastien Trottier · Oct 11, 2012

You can avoid the copy to a new dict by disabling the defaulting feature of defaultdict once you are done inserting new values:

new_data.default_factory = None

Explanation

The template variable resolution algorithm in Django will attempt to resolve new_data.items as new_data['items'] first, which resolves to an empty list when using defaultdict(list).

To disable the defaulting to an empty list and have Django fail on new_data['items'] then continue the resolution attempts until calling new_data.items(), the default_factory attribute of defaultdict can be set to None.