【问题标题】:Split queryset or get multiple querysets by field instead of just ordering by field拆分查询集或按字段获取多个查询集,而不仅仅是按字段排序
【发布时间】:2012-07-08 10:51:57
【问题描述】:

我想以某种方式查询数据库,而不仅仅是按某个字段排序,而是为该字段的每个唯一值获得一个单独的 QuerySet(或字典、列表等)。希望以下示例对您有所帮助:

假设一个模型像

Class Person(models.Model):
   first_name = models.CharField()
   last_name = models.CharField

调用 Person.objects.all().order_by('last_name') 会给我一个长查询集。相反,我希望为每个唯一的姓氏创建一个单独的列表。因此,每个姓氏="Smith" 的人都有一个列表,每个姓氏="Nguyen" 的人都有另一个列表,等等。

显然我无法提前知道数据库中的姓氏是什么,也不知道有多少人会共享一个共同的姓氏。在 django 中是否有任何快速、高效或自动的方法来执行此操作,还是我只需要在取回一个大型查询集后自己处理数据?

【问题讨论】:

  • order_by last_name,然后使用itertools.groupby 进行迭代也可能是一个选项

标签: python django models


【解决方案1】:

samy 的代码是正确的,但效率很低:您执行 n+1 次查询(其中 n 是唯一姓氏的数量)。由于无论如何您都将获得表中的所有对象,因此您不妨一口气完成。改用这个:

from collections import defaultdict
person_dict = defaultdict(list)
persons = Person.objects.all()
for person in persons:
    person_dict[person.last_name].append(person)

【讨论】:

  • 这仍然会导致多个查询 + 全部查询。我是否错过了为什么会发生这种情况?
【解决方案2】:

查看regroup 标签。

{% regroup persons by last_name as surname_list %}

<ul>
{% for last_name in surname_list %}
    <li>{{ last_name.grouper }}
    <ul>
        {% for person in last_name.list %}
          <li>{{ person.last_name }}, {{ person.first_name }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

(此代码未经测试,请阅读文档并自行解决任何问题)

【讨论】:

  • 谢谢!这真的很有帮助。我试图缓和但我目前缺乏这样做的声誉分数,我会在提高我的声誉后回来。我接受以下答案,因为它在视图而不是模板中完成工作。
  • 我很好奇,为什么你认为在视图中进行分组比在模板中进行分组有什么优势?似乎在模板上进行分组会导致视图中的代码更清晰和性能更好(记住查询集在 django 中是惰性的)。
【解决方案3】:

您可以获得所有唯一的姓氏:

from django.db.models import Count
...
last_names = Person.objects.values('last_name').annotate(Count('last_name')) # This will return all the unique last_names
values = dict( ((last_name['last_name'], Person.objects.all().filter(last_name = last_name['last_name'])) for last_name in last_names if last_name['last_name__count']) )
# This will create a dictionary where the keys are all the unique names and the values are querysect of all the values that have that lastname

Daniel-Roseman 是对的,它效率很低,所以这里有一个调整版本...

from collections import defaultdict
values = defaultdict(list)
_ = map(lambda person: values[person.last_name].append(person), Person.objects.all())

注意_ = ... 是这样我们不会在终端上打印所有None ;)

【讨论】:

    猜你喜欢
    • 2020-08-16
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2012-12-20
    • 2015-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多