如何在Django中预取聚合的@property?

时间:2013-10-01 13:18:17

标签: django orm aggregate

我们有两个型号(简化版):

class Contestant(models.Model):
    email = models.EmailField(max_length=255, unique=True)
    # plus some other fields

    @property
    def total_points(self):
        return self.points.aggregate(total=Sum('value'))['total'] or 0

class Points(models.Model):
    contestant = models.ForeignKey(Contestant, related_name='points')
    value = models.PositiveIntegerField()
    # plus some other fields which determine based on what we
    # awarded ``Points.value``

当我们显示参赛者名单及其total_points价值时 导致对每个结果进行额外查询 - 即执行以下查询:

  1. 获取参赛者名单
  2. 获取第一名选手的total_points
  3. 获取第二名选手的total_points
  4. 我尝试更改查询集以预取数据,如下所示:

    Contestant.objects.filter(...).prefetch_related('points')
    

    ...但是,即使它有效,也不会使用预取数据 列出参赛者(因此每个结果仍然会尝试抓取total_points 在单独的查询中。)

    是否可以:

    • 以某种方式告诉ORM在@property字段时使用预取值 填充单个模型对象的数据(例如,访问Contestant.total_points @property方法中的预取值)?
    • 或以不同的方式预取它们(与上面的示例相反)?
    • 或使用完全不同的方法来实现相同的结果?

    (我在tastypie列出结果,如果重要的话。)

    谢谢。

1 个答案:

答案 0 :(得分:10)

如果您的目标是为每个项目添加汇总值,则应使用annotate,而不是aggregate

例如(一个简单的查询,不需要其他方法):

Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))

如果你真的想把这段代码放在你的查询之外:你可以,但模型方法不是一种正确的方法。模型上的方法用于单个实例上的操作。如果您想在整个QuerySet上执行某些操作,请改用ORM Manager

使用Manager会看起来像这样:

class TotalPointsManager(models.Manager):
    def get_queryset(self):
        return super(TotalPointsManager, self).get_queryset().annotate(total_points=Sum('points__value'))

class Contestant(models.Model):
    email = models.EmailField(max_length=255, unique=True)

    objects = TotalPointsManager() # You are overriding the default manager!

然后你会像往常一样构建你的查询(你可以删除prefetch_related):

Contestant.objects.filter(...)

... total_points字段对于每个对象都会“神奇地”可用。