【问题标题】:Django Aggregate by QuerySet?Django 通过 QuerySet 聚合?
【发布时间】:2014-02-03 08:00:57
【问题描述】:

我正在编写一个联赛系统,我想显示每个赛季的球员排名,按每个球员在该赛季累积的积分排序。

到目前为止,我设法使用类似于以下的代码来做到这一点:

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()

我认为同样可以通过更简单的聚合来实现,但我就是无法正确使用 annotate()。

我试过这个,但它只是总结了整个赛季的所有分数:

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

我错过了什么?我猜 .extra() 可以使用,如果它会导致可移植代码。

【问题讨论】:

  • 请提供完整的模型定义,这样我们就可以看到所有重要的关系也告诉我们你期望从查询集中得到什么样的结果。谢谢。

标签: django django-aggregation


【解决方案1】:

这会返回可用的结果:

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')

【讨论】:

    【解决方案2】:

    Django 的 ORM 并未涵盖所有用例。

    您可以将聚合存储在单独的模型中或回退到原始 SQL。

    【讨论】:

    • 或者你只是分析ORM的行为;)
    猜你喜欢
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 2015-02-16
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多