如何在Django中预取聚合的@property? [英] How to prefetch aggregated @property in Django?
问题描述
我们有两个模型(简化版本):
We have two models (simplified versions):
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
值时,它
导致对每个结果的额外查询 - 即执行以下查询:
When we display a list of contestants along with their total_points
value, it
results in an extra query for each result - i.e. the following queries are performed:
- 获取参赛者列表
- 获取
total_points
第一参赛者的价值 - 获取
total_points
v第二名参赛者的评价 - 等
- fetch list of contestants
- fetch
total_points
value of 1st contestant - fetch
total_points
value of 2nd contestant - etc
我尝试改变查询集,以便如下预取数据:
I tried altering the queryset to prefetch the data as follows:
Contestant.objects.filter(...).prefetch_related('points')
...,但是即使它有效,当
列出参赛者时,预取数据也不会被使用(所以每个结果仍然会尝试在单独的查询中获取 total_points
。
..., however even though it works, the prefetched data is not utilized when
listing contestants (so each result still tries to fetch total_points
in a separate query).
是否可以:
- 以
为单个模型对象填充数据,以某种方式告诉ORM为@property字段使用预取值(例如访问Contestant.total_points
@property方法中的预取值) - 或以不同的方式预取它们(而不是或者使用完全不同的方法实现相同的结果?
- somehow tell the ORM to use prefetched values for the @property field when
populating data for individual model objects (e.g. access the prefetched value inside the
Contestant.total_points
@property method)? - or to prefetch them in a different way (as opposed to the example above)?
- or to use a completely different approach achieving the same result?
(我在 tastypie
中列出结果,如果重要)。
(I'm listing results in tastypie
, if it matters.)
谢谢。
推荐答案
当您的目的是为每个项目添加聚合值时,您应该使用 annotate
/ a>,而不是聚合
。
When your aim is to add aggregated values to each item, you should use annotate
, instead of aggregate
.
例如(一个简单的查询,不需要额外的方法):
For example (a simple query, no additional methods required):
Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))
如果你真的想把这个代码放出您的查询:您可以,但是一种模型方法不是正确的方法。模型上的方法用于单个实例的操作。如果您想在整个QuerySet上执行某些操作,请使用 ORM管理器
有一个经理这样看起来像这样:
With a Manager this would look like this:
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
):
and then you would construct your query as usual (you can drop prefetch_related
):
Contestant.objects.filter(...)
...和 total_points
字段将成为神奇可用于每个对象。
...and total_points
field would become "magically" available for every object.
这篇关于如何在Django中预取聚合的@property?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!