2014-02-03 4 views
0

Я пишу систему лиги, и я хочу отображать рейтинги игроков в каждом сезоне, отсортированные по пунктам, каждый игрок, накопленный в течение этого сезона.Django Aggregate by QuerySet?

До сих пор мне удалось сделать это с кодом, аналогичным образом:

class Player(models.Model): 
    name = models.CharField(max_length=100) 


class Season(models.Model): 
    name = models.CharField(max_length=100) 
    start_date = models.DateField() 
    end_date = models.DateField() 
    players = models.ManyToManyField(Player) 

    def get_player_rank(self, player): 
     return player.matchresult_set.filter(season=self).aggregate(points=Sum('points'))['points'] 

    def get_ranks(self): 
     ranks = [(player, self.get_player_rank(player)) for player in self.players.all()] 
     ranks.sort(key=lambda tup: tup[1]) 
     return ranks 


class Match(models.Model): 
    date = models.DateField() 
    players = models.ManyToManyField(Player, through='MatchResult') 
    season = models.ForeignKey(Season) 


class MatchResult(models.Model): 
    player = models.ForeignKey(Player) 
    match = models.ForeignKey(Match) 
    points = models.IntegerField() 

Я думаю, что то же самое может быть достигнуто за счет более простой агрегации, но я просто не могу получить Аннотировать() правильно.

Я попытался это, но он просто суммируется все точки в течение сезонов:

class Season(models.Model):  
    def get_ranks(self): 
     return self.players.annotate(points=Sum('matchresult__points')).order_by('-points') 

Что мне не хватает? Я думаю, что .extra() можно использовать, если это приведет к переносу кода.

+1

Просьба представить полные определения модели , поэтому мы можем видеть, что все важные отношения также говорят нам, какого результата вы ожидаете от набора запросов. Благодарю. – mariodev

+0

@mariodev: done :) – minder

ответ

0

Это возвращает полезные результаты:

Season.objects.values('name','match__matchresult__player__username').annotate(points=Sum('match__matchresult__points')).distinct() 

Я также необходимо реализовать SumWithDefault, чтобы избавиться от значения NULL:

from django.db.models.sql.aggregates import Aggregate 
from django.db.models import Aggregate as Ag 

class SumWithDefaultSQL(Aggregate): 
    def __init__(self, col, default=None, **extra): 
     super(SumWithDefaultSQL, self).__init__(col, default=default, **extra) 
     self.sql_function = 'SUM' 
     if default is not None: 
      self.sql_template = 'COALESCE(%(function)s(%(field)s), %(default)s)' 


class SumWithDefault(Ag): 
    name = 'Sum' 

    def add_to_query(self, query, alias, col, source, is_summary): 
     aggregate = SumWithDefaultSQL(col, source=source, is_summary=is_summary, **self.extra) 
     query.aggregates[alias] = aggregate 

окончательный запрос:

Season.objects.values('name','match__matchresult__player__username').annotate(points=SumWithDefault('match__matchresult__points', default=0)).distinct().order_by('-points') 
-1

ORM Django не охватывает каждый прецедент.

Вы можете хранить агрегаты в отдельной модели или вернуться к необработанному SQL.

+0

Или вы просто анализируете поведение ORM;) – minder