Malheureusement, ce n'est pas une opération possible puisque (pour moi) le postgresql WHERE
l'opération (filtrer/exclure) réduit les lignes avant que les fonctions d'agrégation ne puissent les utiliser.
La seule solution que j'ai trouvée est de simplement calculer le classement de tous les Person
avec un jeu de requêtes séparé, puis annoter votre jeu de requêtes avec ces résultats.
Cette réponse (voir la méthode améliorée) explique comment "annoter un jeu de requêtes avec des données préparées en externe dans un dict".
Voici l'implémentation que j'ai faite pour vos modèles :
class PersonQuerySet(models.QuerySet):
def total_scores(self):
# compute the global ranking
ranks = (Person.objects
.annotate(total_score=models.Sum('session__gamesession__score'))
.annotate(rank=models.Window(expression=DenseRank(),
order_by=models.F('total_score').decs()))
.values('pk', 'rank'))
# extract and put ranks in a dict
rank_dict = dict((e['pk'], e['rank']) for e in ranks)
# create `WHEN` conditions for mapping filtered Persons to their Rank
whens = [models.When(pk=pk, then=rank) for pk, rank in rank_dict.items()]
# build the query
return (self.annotate(rank=models.Case(*whens, default=0,
output_field=models.IntegerField()))
.annotate(total_score=models.Sum('session__gamesession__score')))
Je l'ai testé avec Django 2.1.3 et Postgresql 10.5, donc le code peut légèrement changer pour vous.
N'hésitez pas à partager une version compatible avec Django 1.11 !