如何在 Django 中预取聚合的@property?
Posted
技术标签:
【中文标题】如何在 Django 中预取聚合的@property?【英文标题】:How to prefetch aggregated @property in Django? 【发布时间】: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
值时,
为每个结果产生一个额外的查询 - 即执行以下查询:
-
获取参赛者名单
获取
total_points
第一个参赛者的值
获取total_points
第二位参赛者的值
等
我尝试更改查询集以预取数据,如下所示:
Contestant.objects.filter(...).prefetch_related('points')
...,但是即使它有效,预取的数据也不会被使用
列出参赛者(所以每个结果仍然试图获取total_points
在单独的查询中)。
是否可以:
以某种方式告诉 ORM 在以下情况下使用 @property 字段的预取值 为单个模型对象填充数据(例如,访问Contestant.total_points
@property 方法中的预取值)?
或以不同的方式预取它们(与上面的示例相反)?
还是使用完全不同的方法来获得相同的结果?
(如果重要的话,我会在tastypie
中列出结果。)
谢谢。
【问题讨论】:
【参考方案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 或更早版本。以上是关于如何在 Django 中预取聚合的@property?的主要内容,如果未能解决你的问题,请参考以下文章
教义 orm:绕过延迟加载并在 getter 中预取相关记录