Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django - how to get a queryset based on a count of references to foreign field

Question about how to perform a django query. I have a Game model with a 'through' ManyToManyField to the User model.

class Game(models.Model):
    # other misc game-description fields...
    players = models.ManyToManyField(User, through='PlayerGameResult',
                                     blank=True, null=True)

class PlayerGameResult(models.Model):
    game            = models.ForeignKey(Game)
    player          = models.ForeignKey(User)
    dtime_played    = models.DateTimeField(default=datetime.now)
    # ...

If a particular game has been played by 1+ players, then the players field in the Game model will have 1+ references. Otherwise players is null. For the query question please refer to the ?????? below....

# all games not yet played by this user...
games_qset = Game.objects.exclude(players__username=request.user.username)

# out of all the games not played by this user, extract the ones not
# yet played by anyone else either (this user would be the 1st player)
games_unplayed_qset = games_qset.filter(players__isnull=True)


# here's the question: out of all the games that have not been played by 
# this user, get a queryset that extracts only the games that have been 
# played by more than 10 (GAME_WITH_FEW_PLAYERS_THRESHOLD) players
# ?????? this is the query I don't know how to make... ?????
#
# in the following example, I want to get back a queryset that includes only
# Game Ids 6 and 19, because they have players count 20 and 12 (> threshold 10)
#
# (Pdb) for rec in games_qset: print rec
# Game id 6,  Num players:  20
# Game id 7,  Num players:   5
# Game id 13, Num players:   0
# Game id 19, Num players:  12
#
# for a count of all games that have been played by at least one player:
# (Pdb) games_qset.filter(players__isnull=False).count()
# 3
#

Any ideas? Thank you. Juan

like image 491
jd. Avatar asked Feb 22 '11 15:02

jd.


People also ask

How do I get QuerySet in Django?

You get a QuerySet by using your model's Manager . Each model has at least one Manager , and it's called objects by default. Access it directly via the model class, like so: >>> Blog.objects <django.db.models.manager.Manager object at ...> >>> b = Blog(name='Foo', tagline='Bar') >>> b.objects Traceback: ...

Is Django QuerySet lazy?

This is because a Django QuerySet is a lazy object. It contains all of the information it needs to populate itself from the database, but will not actually do so until the information is needed.

How do you annotate in Django QuerySet?

Appending the annotate() clause onto a QuerySet lets you add an attribute to each item in the QuerySet, like if you wanted to count the amount of articles in each category. However, sometimes you only want to count objects that match a certain condition, for example only counting articles that are published.

How do you count in Django ORM?

Use Django's count() QuerySet method — simply append count() to the end of the appropriate QuerySet. Generate an aggregate over the QuerySet — Aggregation is when you "retrieve values that are derived by summarizing or aggregating a collection of objects." Ref: Django Aggregation Documentation.


2 Answers

It's confusing when you say "extract" as in exclude.

The answer is in annotation and aggregation.
http://docs.djangoproject.com/en/dev/topics/db/aggregation/

from django.db.models import Count
Games.objects.annotate(num_players=Count('players')).filter(num_players__gt=10)
like image 120
Yuji 'Tomita' Tomita Avatar answered Sep 19 '22 14:09

Yuji 'Tomita' Tomita


I think you are looking for annotate - based on example in docs

q = Game.objects.filter(players__isnull=False).annotate(Count('players'))
q[0].players__count #prints first games players

or to filter by count

q = Game.objects.annotate(num_players=Count('players')).filter(num_players__gt=1)
like image 24
JamesO Avatar answered Sep 20 '22 14:09

JamesO