merge querysets in django

14,848

Solution 1

This should do the trick:

# On the top of the file:
from django.db.models import Q

# Game instance method:
def get_all_players(self):
    return Player.objects.filter(Q(games1__pk=self.pk) | Q(games2__pk=self.pk))

Q is described in details here: Complex lookups with Q objects.

Solution 2

For a perhaps more semantically clear solution:

def get_all_players(self):
    return (self.players1.all() | self.players2.all()).distinct()
Share:
14,848
imkost
Author by

imkost

Updated on June 04, 2022

Comments

  • imkost
    imkost almost 2 years

    I have in models.py:

    class Game(models.Model):
        players1 = models.ManyToManyField(Player, related_name='games1')
        players2 = models.ManyToManyField(Player, related_name='games2')
    
        def get_all_players(self):
            return list(itertools.chain(self.players1.all(), self.players2.all()))
    

    How can I write same get_all_players method, but return QuerySet, not list?

    P.S. I know that there is | operator:

    def get_all_players(self):
        return self.players1.all() | self.players2.all()
    

    But it works in a very strange way. Result of this function contains more players than there are in players1 + players2 (result contains repeats of some players)

  • imkost
    imkost over 11 years
    Thank you very much! I didn't know that games1 in Q will iterate all games. Tell me, why do you use games1__pk=self.pk instead of games1=self? Are there any advantages in this way?
  • Tadeck
    Tadeck over 11 years
    @imkost: I am just used to it. I believe games1=self may be implicitly translated to games1__pk=self.pk (if it gives you the same result). Having __ in the argument name also makes it clear that there are some JOINs involved in the query.
  • Kye
    Kye over 7 years
    This is definitely the more clean solution.