Django模板无法循环defaultdict


70
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

这是输出:

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

这是模板:

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

但是模板中的循环不起作用。什么都没有出现。它甚至没有给我一个错误。我怎样才能解决这个问题?应该显示姓氏和名字,像这样:

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

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

您尚未显示将字典放入模板上下文的代码。您确定这是正确的吗?
Ned Batchelder

是的,其他所有内容都可以在循环之外正确呈现。
user216171 2011年

Answers:



91

完成插入新值后,可以通过禁用defaultdict的默认功能来避免复制到新dict :

new_data.default_factory = None

说明

在Django模板变量解析算法将尝试解析new_data.itemsnew_data['items']第一,使用时其解析为一个空列表defaultdict(列表)

要禁用默认列表为空列表并让Django失败,new_data['items']然后继续尝试解析直到调用new_data.items(),可以将defaultdictdefault_factory属性设置为None


6
+1-这比所选答案要有效得多,尤其是对于大型词典而言。
keithhackbarth

很好的答案,解释有助于很多理解底层的问题!
Blackeagle52 2014年

我可能会添加一个{%,在detauldictvar%中为k,v},即使您没有将default_factory设置为None,也可以迭代defaultdict的元素。通过Sebastien的解释,现在我可以理解为什么了:-)
RobM 2015年

1

由于“问题”在几年后仍然存在,并且是Django模板工作方式的继承者,因此我更愿意编写一个新答案,以详细说明为什么此行为保持原样。

错误修复方法

首先,解决方案是将defaultdict转换为,dict然后再将其传递到模板上下文:

context = {
    'data': dict(new_data)
}

您不应defaultdict在Django的模板上下文中使用对象。

但为什么?

以下Django问题#16335中详细说明了此“错误”的原因:

实际上,可以归结为以下事实:模板语言对字典和属性查找使用相同的语法。

...并从文档中

字典查找,属性查找和列表索引查找均以点表示法实现。[...]如果变量解析为可调用对象,则模板系统将不带任何参数调用它,并使用其结果代替可调用对象。

当Django解析您的模板表达式时,它将首先尝试data['items']。但是,这是一个有效的表达式,它将items在defaultdict中自动创建一个新条目data,并用一个空列表初始化(在原始作者的情况下),并返回创建的列表(空)。

目的是在items没有实例参数的情况下调用该方法data(简称:)data.items(),但是由于data['items']是有效表达式,因此Django停在那里并获取刚创建的空列表。

如果您尝试使用相同的代码data = defaultdict(int),则会得到一个TypeError: 'int' object is not iterable,因为Django无法迭代由创建新的条目返回的“ 0”值defaultdict

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.