在连接表中使用django中的Filtered Count会返回错误的值

时间:2018-02-07 09:29:21

标签: django orm count distinct aggregation

为了简单起见我有四个表(A,B,类别和关系),关系表在B中存储A的强度,而类别存储B的类型。

A <--- Relation ---> B ---> Category

(所以A和B之间的关系是n到n,其中B和Category之间的关系是n到1)

我需要的是计算使用以下方法获得的类别中A的发生率:

A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    ANum = Count('id', distinct=False)
)

请注意,如果我使用&#39; distinct = True&#39;相反,每一个和每一个Anum&#39;等于1,这不是理想的结果。问题是我必须根据B发生的日期(以及B表中的其他一些字段)过滤计算, 我正在使用django 2.0的功能,这使using filter as an argument in aggregation成为可能。 我们假设:

kwargs= {}
kwargs['relation_set__B____BDate__gte'] = the_start_limit

我可以在我的代码中使用它,如:

A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    Anum = Count('id', distinct=False, filter=Q(**kwargs))
)

然而,由于表连接,我得到的结果是重复的,我不能使用distinct = True,正如我解释的那样。 (查询A也是必须的,因为我必须按照我的question here中的说明聚合此表中的其他一些字段)

我正在使用Postgres和django 2.0.1。

是否有任何变通方法可以实现我的想法?

更新

使用另一个子查询完成它:

# subquery
annotation = {
    'ANum': Count('relation_set__A_id', distinct=False, 
    filter=Q(**Bkwargs),
}
sub_filter = Q(relation_set__A_id=OuterRef('id')) & 
Q(Category_id=OuterRef('relation_set__B__Category_id'))
# you could annotate 'relation_set__B__Category_id' to A query an set the field here.
subquery = B.objects.filter(
    sub_filter
).values(
    'relation_set__A_id'
).annotate(**annotation).values('ANum')[:1]

# main query
A.objects.values(
    'id', 'relation_set__B__Category_id'
).annotate(
    Anum = Subquery(subquery)
)

1 个答案:

答案 0 :(得分:1)

我还不确定我是否明白你想要的东西。你写了

  

请注意,如果我使用&#39; distinct = True&#39;相反,每一个和每一个Anum&#39;将等于1

当然。您将关联的A对象计数到每个A对象。每个都是自我的。所以我仍然认为你不想用Anum注释A对象,但可能是类别。这个应该在每个类别中为您提供所需数量的As。

Category.objects.annotate(
    Anum=Count(
        'b__relation__a',
        filter=Q(b__BDate__gte=the_start_limit),
        distinct=True
    )
)

'b__relation__a'跟在relations backwards之后,并选择与该类别相关的所有A对象。但是,过滤器将计算的关系限制为某些Bs。需要distinct=True才能避免query bug

如果你真的想要&#34;按其id&#34;分组的A对象列表(并且不仅仅是汇总的Anum - 计数),正如您在评论中所述,我在单个查询中看不到一种简单的方法。