使用Django避免O(n)查询

时间:2009-09-29 07:48:05

标签: django django-models complexity-theory

我有这样的模特:

class PledgeItem(models.Model):
    title = models.CharField(...)
    usd_amount = models.DecimalField(...)

class Pledger(models.Model):
    name = models.CharField(...)
    ...

class Pledge(models.Model):
    pledger = models.ForeignKey(Pledger)
    item    = models.ForeignKey(PledgeItem)
    usd_amount = models.DecimalField(...)
    ...

我的PledgeItem有一种方法可以确定承诺的百分比(例如,一项可能需要花费100美元,并且每项承诺为20美元,这意味着它已承诺60%):

 class PledgeItem(models.Model):
     ...
     def percentage_pledged(self):
         pledge_total = Pledge.objects.filter(item = self).sum(usd_amount)
         return (pledge_total / self.usd_amount) * 100

出于这个问题的目的,请假设我正确处理self.usd_amount为零,以及Pledges上没有PledgeItem的情况(尽管我不得不问,为什么sum(field)在这些情况下会返回None?)。

问题是,如果我在percentage_pledged n列表中拨打PledgeItems,则每个PledgeItem都会有一个查询。有没有一种优雅的解决方法,而不使用save信号来更新percentage_pledged字段?如果我能以某种方式预取这些数据(即一次获取所有Pledges,然后循环遍历它们),那就太好了。

我不确定解决方案看起来会是什么样的(例如,那套Pledges会在哪里生活?),但我确信这是一个常见的问题(而且这个问题一直困扰着我之前),所以我想我会看到人们对Django更有经验的解决方案。也许save信号是它所属的地方,特别是对于“低写,高读”类型的站点。

1 个答案:

答案 0 :(得分:3)

这是Django 1.1中新聚合功能的工作。

您希望将“pledge_sum”字段“注释”到查询集中的每个PledgeItem。这很容易做到:

from django.db.models import Sum
PledgeItems.objects.all().annotate(pledge_sum=Sum(pledge__usdamount))

显然,您可以使用您想要的任何过滤器替换all()

您仍然需要对每个PledgeItem进行百分比计算,但不会产生任何额外的查询。