Django模板不能循环defaultdict
Posted
技术标签:
【中文标题】Django模板不能循环defaultdict【英文标题】:Django template can't loop defaultdict 【发布时间】:2011-06-13 10:36:09 【问题描述】: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>
【问题讨论】:
您没有显示将字典放入模板上下文的代码。你确定这一切正常吗? 是的,其他一切都在循环之外正确呈现。 【参考方案1】:在插入新值后,您可以通过禁用 defaultdict 的默认功能来避免复制到新字典:
new_data.default_factory = None
说明
template variable resolution algorithm in Django 将首先尝试将new_data.items
解析为new_data['items']
,当使用defaultdict(list) 时会解析为一个空列表。
要禁用默认为空列表并让 Django 在 new_data['items']
上失败然后继续解析尝试直到调用 new_data.items()
,default_factory attribute of defaultdict 可以设置为 None。
【讨论】:
+1 - 这比选择的答案更有效,尤其是对于大型字典集。 很好的答案,解释有助于理解底层问题! 我可能会补充一点,% for k, v in detauldictvar % 允许您迭代 defaultdict 的元素,即使没有将 default_factory 设置为 None。现在有了 Sebastien 的解释,我可以理解为什么 :-) 【参考方案2】:尝试:
dict(new_data)
在 Python 2 中,最好使用 iteritems
而不是 items
:)
【讨论】:
我无法相信像这样简单的事情。非常感谢! 它实际上是作为针对 django 的错误提交的:code.djangoproject.com/ticket/16335 但事实证明,唯一好的解决方案确实是将其转换为字典,因为它现在显示在 docs 中,并且在changeset【参考方案3】:由于“问题”多年后仍然存在并且是 Django 模板工作方式所固有的,我更喜欢写一个新的答案,详细说明为什么这种行为保持原样。
如何修复错误
首先,解决方案是将defaultdict
转换为dict
,然后再将其传递给模板上下文:
context =
'data': dict(new_data)
您不应该在 Django 的模板上下文中使用 defaultdict
对象。
但是为什么呢?
下面Django issue #16335详细说明了这个“bug”背后的原因:
确实,归结为模板语言使用相同的语法进行字典和属性查找。
...来自the docs:
字典查找、属性查找和列表索引查找是用点符号实现的。 [...] 如果变量解析为可调用对象,模板系统将不带参数调用它,并使用其结果而不是可调用对象。
当 Django 解析你的模板表达式时它会首先尝试data['items']
。但是,这是一个有效的表达式,它会自动在你的默认字典 data
中创建一个新条目 items
,初始化为一个空列表(在原始作者的情况下)并返回创建的列表(空)。
意图的操作是调用方法items
没有实例data
的参数(简而言之:data.items()
),但由于data['items']
是一个有效的表达式,Django 停在那里并得到空刚刚创建的列表。
如果您尝试使用相同的代码但使用data = defaultdict(int)
,您将得到TypeError: 'int' object is not iterable
,因为Django 将无法迭代创建@987654336 的新条目所返回的“0”值@。
【讨论】:
以上是关于Django模板不能循环defaultdict的主要内容,如果未能解决你的问题,请参考以下文章