【问题标题】:How to prefetch aggregated @property in Django?如何在 Django 中预取聚合的@property?
【发布时间】:2013-10-01 13:18:16
【问题描述】:

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

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第二位参赛者的值

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

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

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

是否可以:

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

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

谢谢。

【问题讨论】:

    标签: django orm aggregate


    【解决方案1】:

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

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

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

    如果您真的想将此代码从查询中删除:您可以,但模型方法不是执行此操作的正确方法。模型上的方法用于单个实例上的操作。如果您想在整个 QuerySet 上做某事,请改用 ORM 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 字段将“神奇地”用于每个对象。

    【讨论】:

    • +1。谢谢,我也大致沿着这些思路思考。我需要检查我们是否真的到处都需要它。让我们等待是否有人提出任何替代方案,但这似乎是我目前所知道的最明智的方法。
    • 请注意,我们最后使用了github.com/initcrash/django-denorm(出于性能原因),但我接受了它,因为它是一种有效的方法。谢谢,伙计!
    • 太棒了,这可行,但只使用 get_query_set 而不是 get_queryset
    • @JasonSack 在 Django 1.6 中更改了名称。您可能使用 Django 1.5 或更早版本。
    猜你喜欢
    • 2018-05-30
    • 1970-01-01
    • 2020-04-27
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 2010-12-20
    • 1970-01-01
    • 2017-02-12
    相关资源
    最近更新 更多