Search code examples
djangomany-to-many

How to query ManyToMany through relationship with some property in django?


class Person(models.Model):
    age = models.IntegerField()

class Group(models.Model):
    people = models.ManyToManyField(Person,through=Membership)

class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    existing = models.BooleanField()

This is the simplified class structure and what I want to query is: A queryset on group such that there are some people with membership such that existing is true. I have tried using Group.objects.filter(membership__existing=True) but it seems to be grabbing all the Groups.

What is the most efficient way to filter out the queryset?


Solution

  • I created a sample app using your model definitions, and the filter you've specified works fine. I had to change the through attribute of the people field to be a string, as Membership isn't defined yet. I also added a name field to Group to make it easier to test.

    class Person(models.Model):
        age = models.IntegerField()
    
    class Group(models.Model):
        name = models.CharField(max_length=255)
        people = models.ManyToManyField(Person, through='Membership')
    
    class Membership(models.Model):
        person = models.ForeignKey(Person)
        group = models.ForeignKey(Group)
        existing = models.BooleanField()
    

    Then in the console:

    >>> p1 = Person.objects.create(age=10)
    >>> p2 = Person.objects.create(age=20)
    >>> has_existing = Group.objects.create(name='has_existing')
    >>> has_non_existing = Group.objects.create(name='has_non_existing')
    >>> has_none = Group.objects.create(name='has_none')
    >>> Membership.objects.create(person=p1, group=has_existing, existing=True)
    >>> Membership.objects.create(person=p2, group=has_non_existing, existing=False)
    >>> existing = Group.objects.filter(membership__existing=True)
    >>> assert(len(existing) == 1)
    >>> assert(existing[0].name == 'has_existing')