Django Manager - 覆盖默认的get_query_set以设置默认的“GROUP BY”

时间:2012-07-13 14:17:39

标签: django django-queryset

我对遗留数据库感到困惑。我想修改默认查询集以便熟练地使用db,为此我需要使用GROUP BY。我知道我可以做到这一点,让我得到我想要的SQL:

query = Variant.objects.all().query
query.group_by = ['name']
return QuerySet(query=query, model=Variant)

这将导致我追求的查询集。所以我构建了一个queryset Manager来帮助我。问题是它正在返回正确的值,但是当我指望它时,它是错误的。

class VariantQuerySet(QuerySet):
    def group_by_name(self):
        self.query.group_by = ['name']
        return self.filter()

class VariantManager(models.Manager):

    def get_query_set(self):
        return VariantQuerySet(self.model, using=self._db)

但是当我开始玩它时..

>>> Variant.objects.filter(project__name__icontains="zam")
[<Variant: RevA>, <Variant: RevA>, <Variant: RevA>, <Variant: revB>, <Variant: RevC_Fiendish>, <Variant: RevA>, <Variant: RevA_tapeout>]
>>> Variant.objects.filter(project__name__icontains="zam").count()
7
>>> Variant.objects.filter(project__name__icontains="zam").group_by_name()
[<Variant: RevA>, <Variant: revB>, <Variant: RevC_Fiendish>, <Variant: RevA_tapeout>]

到目前为止一切顺利。 7个未分组的项目,4个分组。

>>> Variant.objects.filter(project__name__icontains="zam").group_by_name().count()
7

那么为什么我的计数仍然停留在7 - 它应该是4?我认为_result_cache持有该值,所以我在方法中将其设置为None但没有运气。任何想法为什么这是错误的?

1 个答案:

答案 0 :(得分:1)

.count()实际创建了一个新查询,删除了字段并替换为COUNT(*)。实际上不可能按字段进行分组,而在纯SQL中对分组表进行计数。基本上,您最初的查询在SQL中看起来像这样:

SELECT myapp_variant.id, myapp_variant.name, myapp_variant.etc, ...
FROM myapp_variant inner join myapp_project on myapp_variant.project_id = myapp_project.id
WHERE myapp_project.name='zam'
GROUP BY myapp_variant.name

计数查询看起来像这样:

SELECT COUNT(*)
FROM myapp_variant inner join myapp_project on myapp_variant.project_id = myapp_project.id
WHERE myapp_project.name='zam'

请注意,它不能再分组。如果确实如此,您将得到以下结果集:

COUNT
-----
    4 
    1
    1
    1

(在这种情况下,4是RevA记录的数量,然后是其他每个记录的1)

因为当您在聚合查询中 group 时,您告诉SQL为每个分组列中的每个唯一值创建一行。 4种不同的变体名称,所以4条记录!这根本不是你想要的

您可以通过输出Django生成的查询来确认这是否是这个问题:

>>> print Variant.objects.filter(project__name__icontains="zam").group_by_name().query

>>> print Variant.objects.filter(project__name__icontains="zam").group_by_name().count().query

这个问题实际上只有两个解决方案:

  1. 重写group_by_name,以便它不仅仅按字段分组,而是实际返回一个过滤的查询集,每个名称只有一条记录。更难做到
  2. 当您需要对分组查询集进行“计数”时,只需使用len(),而不是

    len(Variant.objects.filter(project__name__icontains="zam").group_by_name())
    

    或者,在模板中:

    {{ grouped_variants|length }}