[英]Optimize Django Queryset for loop
如何优化以下查询集?
[link.goal for link in self.child_links.all()]
我想摆脱for循环,只打了一次数据库。
我有以下代码:
class Goal(models.Model):
name = models.CharField(max_length=300)
progress = models.SmallIntegerField(default=0)
def __str__(self):
return self.name
def calc_progress(self):
progress = 0
subgoals = [link.goal for link in self.child_links.all()]
for subgoal in subgoals:
progress += subgoal.weight * subgoal.progress
weight += subgoal.weight
progress = progress / weight / len(subgoals)
self.progress = int(progress)
class Link(models.Model):
parent_goal = models.ForeignKey(Goal, on_delete=models.CASCADE, related_name="child_links")
goal = models.ForeignKey(Goal, on_delete=models.CASCADE, related_name="parent_links")
weight = models.SmallIntegerField(default=1)
def __str__(self):
return str(self.parent_goal) + "-->" + str(self.goal)
我想摆脱for循环,只打了一次数据库。
好的goal
是一个ForeignKey
,这意味着这是一个传统的N + 1问题,你可以通过使用.select_related(..)
或.prefetch_related(..)
来减少负载:
[link.goal for link in self.child_links.select_related('goal').all()]
Willem是正确的, select_related()
将减少您的数据库查询,但您真正应该尝试做的是使用Django聚合将计算移动到数据库。
from django.db.models import Count, F, Sum
def calc_progress(self):
agg = (
self.child_links
.order_by()
.annotate(
progress=F('goal__weight') * F('goal__progress')
)
.aggregate(
progress_sum=Sum('progress'),
weight_sum=Sum('goal__weight'),
count=Count('id'),
)
)
progress = agg['progress_sum'] / agg['weight_sum'] / agg['count']
self.progress = int(progress)
这是未经测试的,因此可能需要进行一些调整,但一般的想法是,如果在数据库中完成这些计算将更有效,并且将不再需要select_related()
。 此函数仅进行一次数据库查询。
您可能会发现我写的这个Django ORM优化备忘单对这种情况很有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.