Django模板无法循环defaultdict

use*_*171 66 python django loops

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
Run Code Online (Sandbox Code Playgroud)

这是输出:

defaultdict(<type 'list'>, {'Smith': ['John', 'Samantha'], 'Spencer': ['shawn']})
Run Code Online (Sandbox Code Playgroud)

这是模板:

{% for lastname, firstname in data.items %}
  <h1> {{ lastname }} </h1>
  <p> {{ firstname|join:", " }} </p>
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

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

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

<h1> Spencer </h1>
<p> shawn </p>
Run Code Online (Sandbox Code Playgroud)

Séb*_*ier 84

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

new_data.default_factory = None
Run Code Online (Sandbox Code Playgroud)

说明

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

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

  • +1 - 这比选定的答案更有效,特别是对于大字典集. (6认同)
  • 很好的答案,解释对理解底层问题有很大帮助! (2认同)

vir*_*ilo 41

尝试:

dict(new_data)
Run Code Online (Sandbox Code Playgroud)

而在Python 2中,最好使用iteritems而不是items:)

  • 它实际上是作为一个反对django的错误提交的:https://code.djangoproject.com/ticket/16335但事实证明唯一的好解决方案确实是将其转换为dict,因为它现在显示在[docs](https ://docs.djangoproject.com/en/dev/topics/templates/#variables),它在[changeset]中可见(https://code.djangoproject.com/ticket/16335) (7认同)
  • 我无法相信这样简单的事情.非常感谢! (2认同)

Max*_*ant 5

由于“问题”在多年后仍然存在并且是 Django 模板工作方式的固有特性,我更喜欢写一个新的答案,详细说明为什么这种行为保持原样。

如何修复错误

首先,解决方案是在将其传递给模板上下文之前将其defaultdict转换为 a dict

context = {
    'data': dict(new_data)
}
Run Code Online (Sandbox Code Playgroud)

你不应该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 将无法迭代创建defaultdict.