Bohužel to není možná operace, protože (pro mě) postgresql WHERE
operace (filtrovat/vyloučit) zúží řádky předtím, než na nich mohou fungovat agregační funkce.
Jediné řešení, které jsem našel, je jednoduše vypočítat hodnocení pro všechny Person
pomocí samostatné sady dotazů a poté anotaci sady dotazů s těmito výsledky.
Tato odpověď (viz vylepšená metoda) vysvětluje, jak „anotovat sadu dotazů externě připravenými daty v diktátu“.
Zde je implementace, kterou jsem vytvořil pro vaše modely:
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')))
Testoval jsem to s Django 2.1.3 a Postgresql 10.5, takže se vám kód může lehce změnit.
Neváhejte a sdílejte verzi kompatibilní s Django 1.11!