如何计算两个m2m关系中相关实体的数量与Django中的相同模型

时间:2018-03-16 14:43:08

标签: python sql django postgresql

大致给出这两个模型:

class Person(models.Model):

    name = models.CharField()


class Resource(models.Model):

    people_contributing = models.ManyToManyField(
        Person, 
        related_name='resouces_contributing_to'
    )

    people_involved = models.ManyToManyField(
        Person, 
        related_name='resources_involved_in'
    )

对于所有想要获得资源数量的人,他/她要么参与 OR

我尝试了以下内容:

resources = Resource.objects.all()

participations = Person.objects.filter(
    Q(resources_contributing_to__in=resources) |
    Q(resources_involved_in__in=resources)
).values(
    # group results by person
    'pk'
).annotate(
    count=Count('id')
).values_list(
    'pk',
    'name',
    'count'
).order_by('-count')

print(participations)

这给了我一个像这样的元组列表:

[
    # (id, name, count)
    (1, 'John Doe', 12),
    (2, 'Jane Doe', 5),
    (3, 'Donald Trump', 3),
]

但是,如果一个人既贡献又参与,则资源将被计算两次,因为资源将连接两次到人员表。我想要的是,如果资源只存在于两种关系中,那么它只被计算一次。

如何更改查询集以防止此情况?

我正在使用postgresql和Django 1.11。

1 个答案:

答案 0 :(得分:2)

计算出现在任一关系中的条目可以通过计算第一关系中的条目+来自第二关系的计数条目来实现 - 计算来自两个关系的条目。这可以通过这个查询集在Django中实现:

participations = Person.objects.filter(
    Q(resources_contributing_to__in=resources) |
    Q(resources_involved_in__in=resources)
).annotate(
    count=Count('resouces_contributing_to__id', distinct=True) + Count('resources_involved_in__id', distinct=True) - Count(Case(When(resources_involved_in__id=F('resouces_contributing_to__id'), then='resources_involved_in__id')), distinct=True),
).values_list(
    'pk',
    'name',
    'count'
).order_by('-count')