多对多计数并优化查询

时间:2018-08-01 11:53:11

标签: django python-3.x

我有一个Word模型 它有很多领域 Phrase模型

我以json格式将字词传递给前端,如下所示:

words_json = []

words = Word.objects.filter(....filter_by_some_params)

    for word in words:
      single_word_json = {}
      single_word_json['id'] = word.id
      single_word_json['name'] = word.name
      single_word_json['count'] = len(word.phrases.all())
      words_json.appen(word_json)

 return words_json

我觉得这样是无效的,并且无法扩展(我将有1万个单词和短语,所以我该怎么办?

1 个答案:

答案 0 :(得分:1)

您可以使用以下方法:

from django.db.models import Count

qs = Word.objects.filter(..).annotate(
    count=Count('phrase')
).values('id', 'name', 'count')

那么您的words_json就是:

words_json = list(qs)

.values()将构建字典查询集。每个词典将包含三个项目:idnamecountcount源自.annotate(..)函数,我们在其中计算phrase的数量。

这样做的好处是我们将在单个查询中完成所有处理,例如:

SELECT w.id, w.name, COUNT(wp.phrase_id)
FROM word AS w
LEFT OUTER JOIN word_phrase AS wp ON w.id = wp.word_id
GROUP BY w.id, w.name

因此,我们而不是 n + 1 个查询(其中 n 个是从Word中出来的.filter(..)个),因此,我们执行一个查询。此外,我们限制了数据库返回结果所需的带宽,并让Django为我们将响应包装成字典。

编辑:如果WordForeignKey的范围是Group,并且您还希望包含该名称,则可以将其写为:

from django.db.models import Count, F

qs = Word.objects.filter(..).annotate(
    count=Count('phrase'),
    group_name=F('group__name')
).values('id', 'name', 'count', 'group_name')

现在,每个字典都将有一个额外的键group_name。如果Word没有Group,则group_name将是None(或JSON中的null)。