我正在创建一个Django应用程序来追踪一个人获得的奖励。下面是我所拥有的两个模型的简化表示:
class AwardHolder(models.Model):
name = models.CharField()
def get_total_awards(self):
entries = self.award_set.all()
calc = entries.aggregate(sum=Sum('units_awarded'))
return calc.get('sum') or 0
class Award(models.Model)
date_awarded = models.DateField()
units_awarded = models.IntegerField()
award_holder = models.ForeignKey(AwardHolder)
我为奖项持有人创建了一个摘要页面,在那里他可以看到他的总奖励。我使用上面的get_total_awards
函数。这一切都很好,但后来我创建了一个概述页面来显示每个奖项持有者的总奖励。我使用以下功能获得每个奖项持有者的总奖励:
def get_all_awards():
qs = AwardHolder.objects.all()
awards = []
for ah in qs:
awards.append((ah, ah.get_total_awards())
return awards
这会生成大量查询,因为它会在每个循环中访问db。有没有办法可以使用prefetch_related
或其他一些db技巧来减少数据库查询的数量,而不必重写get_all_awards()
?
我使用的实际代码有很多字段,比这个例子更复杂。大约有10个类似于get_all_awards()
的函数,所以重写它将是相当有用的。但是,对于100个奖项持有者,我的代码产生了高达18000个查询,所以我知道我必须以某种方式解决这个问题。
答案 0 :(得分:1)
get_all_awards
不一定要致电get_total_awards
。您正在为每个实体重复相同的查询,而不是使用Django已经提供的功能:annotate
。
您可以修改get_all_awards
以使用annotate
,并仅在奖励适用于单个实体时使用get_total_awards
。
def get_all_awards():
qs = AwardHolder.objects.annotate(sum=Sum('award__units_awarded'))
awards = []
for ah in qs:
awards.append((ah, ah.sum))
return awards
您甚至可以放弃for
循环并直接使用查询集qs
中的结果。
因此,在获取多个对象及其相关对象时,您可以获得优化查询的优势,而不是为每个实体运行单独的查询。
答案 1 :(得分:1)
使用模型管理器始终注释AwardHolders
class AwardHolderQuerySet(models.QuerySet):
def all(self):
return self.annotate(Count('award__units_awarded'))
class AwardHolder(models.Model):
name = models.CharField()
objects = AwardHolderQuerySet.as_manager()
...