7

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

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. ETC

我尝试更改查询集以预取数据,如下所示:

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

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

是否有可能:

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

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

谢谢你。

4

1 回答 1

11

当您的目标是为每个项目添加聚合值时,您应该使用annotate,而不是aggregate

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

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

如果您真的想将此代码从查询中删除:您可以,但是模型方法不是执行此操作的正确方法。模型上的方法用于单个实例上的操作。如果您想对整个 QuerySet 做某事,请改用ORM 管理器

使用经理,这将如下所示:

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!

然后你会像往常一样构造你的查询(你可以 drop prefetch_related):

Contestant.objects.filter(...)

...并且total_points场将变得“神奇地”可用于每个对象。

于 2013-10-01T13:43:01.497 回答