8

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
  • 8,033
  • 7
  • 29
  • 47

2 Answers2

12

For a perhaps more semantically clear solution:

def get_all_players(self):
    return (self.players1.all() | self.players2.all()).distinct()
linuxhackerman
  • 300
  • 2
  • 8
10

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.

Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • 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? – imkost Jan 07 '13 at 05:20
  • @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 `JOIN`s involved in the query. – Tadeck Jan 07 '13 at 05:30