如何在加入它们之前限制具有n到n关系的表中的记录,以避免在django ORM中出现重复?

时间:2018-02-12 08:18:47

标签: django filter orm jointable prefetch

延长我之前的question on stack-overflow。我有四张桌子:

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

(所以A和B之间的关系是n到n,其中B和Category之间的关系是n到1) 关系存储B中A的“强度”。我需要计算每个类别中A的强度,并找到最大结果。可以使用以下方法实现:

A.objects.values(
    'id', 'Relation_set__B__Category_id'
).annotate(
     AcIntensity=Sum(F('Relation_set__Intensity'))
).aggregate(
     Max(F('AcIntensity'))
)['AcIntensity__max']

现在我需要根据B中的某些字段过滤强度:

A.objects.values(
    'id', 'Relation_set__B__Category_id'
).filter(
    Relation_set__B__BType=type_filter
).annotate(
    AcIntensity=Sum(F('Relation_set__Intensity'))
).aggregate(
    Max(F('AcIntensity'))
)['AcIntensity__max']

但是我需要避免由于表连接导致计算混乱而导致重复。(在那些定义过滤的字段旁边,我不需要B中的任何字段)
有没有办法使用Django ORM来实现这个目标?

更新
我认为我需要的是在查询数据库之前限制Relation表中的记录(基于B过滤器)。我怎么能这样做?
(也许使用Prefetch_related和Prefetch?)

1 个答案:

答案 0 :(得分:0)

最后我使用conditional aggregation完成了它 您可以在此stack-overflow post中找到更多详细信息。 所以最终的代码是:

result = A.objects.values(
    'id', 'Relation_set__B__Category_id'
).annotate(
    AcIntensity=Sum(
        Case(
            When(
               q_statement_for_filter,then=F('Relation_set__Intensity'),
            ),
            default=0,
            output_field=FloatField()
        )
    )
).exclude(
    AcIntensity=0
).aggregate(
    Max(F('AcIntensity'))
)['AcIntensity__max']

请注意,'q_statement_for_filter'不能为空Q()。