使用 Django 避免 O(n) 查询

发布于 2024-08-05 21:42:17 字数 1363 浏览 6 评论 0原文

我有这样的模型:

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 美元,并且有 3 个承诺,每个承诺 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 为零,以及 PledgeItem< 上没有 Pledges 的情况/code> (尽管我不得不问,为什么 sum(field) 在这些情况下返回 None ?)。

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

我不确定解决方案会是什么样子(例如,那一组Pledges会放在哪里?),但我确信这是一个常见问题(也是一个困扰我的问题)之前),所以我想看看那些对 Django 更有经验的人是如何解决这个问题的。也许save信号就是它所属的地方,特别是对于“低写入,高读取”类型的站点。

I've got models like this:

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(...)
    ...

My PledgeItem has a method to work out what percentage of it is pledged (e.g. an item might cost $100, and have 3 pledges of $20 each, meaning it is 60% pledged):

 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

For the purposes of this question, please assume I properly handle self.usd_amount being zero, and the case where there are no Pledges on the PledgeItem (though I've got to ask, why does sum(field) return None in those cases?).

Problem is, if I call percentage_pledged in a list of n PledgeItems, I have one query per PledgeItem. Is there an elegant way to resolve this without using save signals to update a percentage_pledged field? It'd be nice if I could prefetch that data somehow (i.e. fetch all Pledges in one go, and then loop through them).

I'm not sure what a solution would even look like (for example, where would that set of Pledges live?), but I'm sure this is a common issue (and one that has bugged me before), so I thought I'd see how people more experienced with Django have solved it. Maybe save signals are where this belongs, particularly for "low-write, high-read" type sites.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

早乙女 2024-08-12 21:42:17

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

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

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

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

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

This is job for the new aggregation features in Django 1.1.

You want to 'annotate' a pledge_sum field to every PledgeItem in a queryset. This is easily done:

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

Obviously you can replace all() with whatever filters you want.

You'll still need to do the percentage calculation on each PledgeItem, but it won't result in any extra queries.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文