Django 聚合计数

Posted

技术标签:

【中文标题】Django 聚合计数【英文标题】:Django Aggregation Count 【发布时间】:2019-08-05 00:02:59 【问题描述】:

我正在尝试使用聚合函数过滤我的模型。

我有一个模型 A 和一个模型 B,模型 A 上有一个外键。

annotate_pool = queryset.annotate(nb_bets=Count('bets')).all()
for obj in annotate_pool:
    bets_obj = obj.bets.all()
    bets_length = len(bets_obj)
    print(obj.nb_bets, bets_length)

并且注释没有给我与函数长度相同的结果。

1 1
1 2
1 2
1 2
1 2
1 1
1 1
2 2

这是我的模型:

class Pronostic(models.Model):
    cote_total = models.FloatField(default=0.0)
    trust = models.IntegerField()
    mise_ratio = models.IntegerField(default=10)
    safe = models.BooleanField(default=False)

class Bet(models.Model):
    name = models.CharField(max_length=255)
    match = models.ForeignKey('pronostics.Match', on_delete=models.CASCADE, related_name='bets')
    cote = models.FloatField()
    status = models.IntegerField(choices=STATUS, default=0)
    pronostic = models.ForeignKey('pronostics.Pronostic', related_name='bets', on_delete=models.CASCADE)

len(bets_obj) 应该给我与Count('bets') 相同的结果。 到底是怎么回事?为什么Count 给我一个错误的结果?

提前谢谢你。

编辑:

我正在使用 django-rest-framework,并尝试添加自定义过滤器。 (see doc here)。 预期的结果是: obj.nb_bets 应该等于 bets_length。因为我想像这样过滤我的模型:

queryset.annotate(nb_bets=Count('bets')).filter(nb_bets__gte=2)

queryset.annotate(nb_bets=Count('bets')).filter(nb_bets__lte=2)

这是我的查询集中包含的 SQL 查询:

SELECT "pronostics_pronostic"."id",
       "pronostics_pronostic"."cote_total",
       "pronostics_pronostic"."trust",
       "pronostics_pronostic"."mise_ratio",
       "pronostics_pronostic"."safe"
FROM "pronostics_pronostic"
LEFT OUTER JOIN "pronostics_bet" 
       ON ("pronostics_pronostic"."id" = "pronostics_bet"."pronostic_id")
LEFT OUTER JOIN "pronostics_match" 
       ON ("pronostics_bet"."match_id" = "pronostics_match"."id")
WHERE ("pronostics_pronostic"."visible" = TRUE
       AND "pronostics_pronostic"."safe" = TRUE)
ORDER BY "pronostics_match"."date" DESC

如果您需要更多信息,请告诉我。

【问题讨论】:

能否请您更新 models.py 以及您需要什么输出? 对不起,我更新了我的帖子。 我不确定是否是这种情况,但在创建 annotate_pool 时尝试删除 .all()。看看结果是否一样?离开queryset.annotate(nb_bets=Count('bets')) 不,这不是问题:/ 你能提供你的查询集吗? 【参考方案1】:

根据您发布的 SQL,您的 Pronostic 对象按相关 Match 对象中的字段 date 排序。如果您删除排序(使用空的order_by()),我希望数字匹配。测试一下:

annotate_pool = queryset.order_by().annotate(nb_bets=Count('bets')).all()

【讨论】:

非常感谢!你能解释一下 order_by 如何改变结果吗?因为我仍然希望它们井井有条……因为 Dajngo 休息框架告诉我:UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list 好吧,什么是“好订单”?您只能按查询中的字段排序。【参考方案2】:

只要您不显示annotate_pool 的查询集,这就是一个技巧问题。

我的猜测是,您的 annotate_pool 的 SQL 查询中有来自 Bet 的字段,导致重复的 Protostics 仅由相关的 Bet 字段区分,这再次导致下注数减少.测试:

Pronostic.objects.count() < len(annotate_pool)

True 还是 False?我的猜测是True(前提是annotate_pool 未经过滤并包含所有Pronostic 对象)。这意味着您在 annotate_pool 中有重复的 Pronostics。 Bets 的数量分布在这些重复项上。

【讨论】:

值得一提的是,使用 queryset.distinct() 应该可以为 OP 解决这个问题。 不,distinct 在 SQL 级别(不是主对象级别)工作,如果 SQL 中包含的相关字段是唯一的,则无济于事,请参阅 this warning in the docs。 distinct 应该适用于SELECT 中的所有列。如果它是多对多,则不会包含在允许 distinct 过滤到唯一实例的那组列中。 抱歉,“重复”可能令人困惑。我的意思是包含主要对象 (Pronostic) 的查询集包含多个具有相同主键的 Pronostic 对象;然而,底层查询行通常是唯一的,因为它们包含连接表中的字段。 是的,最后知道这会很有趣; OP保持悬念。

以上是关于Django 聚合计数的主要内容,如果未能解决你的问题,请参考以下文章

Mongo 聚合计数子文档(计数回复评论)

是维度上的聚合(计数),而不是 Druid 支持的指标上的聚合(计数)

Mongo 聚合游标和计数

mongodb - 聚合游标计数

GraphQl 计数聚合条目

计数与季度聚合不同