Search code examples
pythondjangodjango-querysetmanytomanyfield

Querying ManyToMany Relationship in Django


Suppose I have the following models, where Questions and Choices have a many-to-many relationship, and Choices and Voters have a many-to-many relationship. (To understand it better, consider a poll where each Question can have multiple Choices and each Choice can be associated to multiple Questions, and where each Choice stores a list of people who Voted for it, and Voters could have voted for multiple Choices.)

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    choices = models.ManyToManyField('Choice')

class Choice(models.Model):
    choice_text = models.CharField(max_length=200)
    voters = models.ManyToManyField('Voter')

    def __str__(self):
        return self.choice_text

class Voter(models.Model):
    name = models.CharField(max_length=500, default='FNU')

    def __str__(self):
        return self.name

Given a Question object ques1, I want to be able to get a list of all Voters associated to the Choices for that question, i.e. I want a QuerySet of all Voters who voted for one or more of the Choices associated to the Question object ques1.

Therefore if Choices c1 and c2 are associated to Question ques1, and Voters v1 and v2 voted for Choice c1 and Voter v3 voted for Choice c2, then running the query on Question ques1, I would want a QuerySet containing [v1, v2, v3].

Is there any query for this?

The highly-inefficient workaround of course is to iterate through ques1.choices, and update a running list of all voters with each iteration. I guess it could be made slightly more efficient by using select_related, but not a spot on having a clean query.


Solution

  • We can do:

    Voter.objects.filter(choice__in=ques1.choices.all()).distinct()    # Used distinct() to get unique voters.
    

    Read more about it in Django's official documentation.