简体   繁体   中英

How to count the number of related entities over two m2m relations to the same Model in Django

Given roughly these two models:

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'
    )

For all persons I want to get the count of resources he/she is either contributing to OR involved in.

I tried the following:

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)

This gives me a list of tuples like this:

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

However if a person is both contributing and involved the resource will be counted twice because the resource will be joined twice to the person table. What I want is the resource to be counted only once if it is present in both relationships.

How do I have to change my queryset to prevent this?

I am using postgresql and Django 1.11.

Counting entries that are appearing in either of relations can be achieved by counting entries from 1st relation + counting entries from 2nd relation - counting entries from both relations. That can be achieved in Django by this queryset:

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')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM