如何在 Django 中预取聚合的@property?

Posted

技术标签:

【中文标题】如何在 Django 中预取聚合的@property?【英文标题】:How to prefetch aggregated @property in Django? 【发布时间】:2013-10-01 13:18:16 【问题描述】:

我们有两个模型(简化版):

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 值时, 为每个结果产生一个额外的查询 - 即执行以下查询:

    获取参赛者名单 获取total_points第一个参赛者的值 获取total_points第二位参赛者的值 等

我尝试更改查询集以预取数据,如下所示:

Contestant.objects.filter(...).prefetch_related('points')

...,但是即使它有效,预取的数据也不会被使用 列出参赛者(所以每个结果仍然试图获取total_points 在单独的查询中)。

是否可以:

以某种方式告诉 ORM 在以下情况下使用 @property 字段的预取值 为单个模型对象填充数据(例如,访问 Contestant.total_points @property 方法中的预取值)? 或以不同的方式预取它们(与上面的示例相反)? 还是使用完全不同的方法来获得相同的结果?

(如果重要的话,我会在tastypie 中列出结果。)

谢谢。

【问题讨论】:

【参考方案1】:

当您的目标是为每个项目添加聚合值时,您应该使用annotate,而不是aggregate

例如(一个简单的查询,不需要额外的方法):

Contestant.objects.filter(...).annotate(total_points=Sum('points__value'))

如果您真的想将此代码从查询中删除:您可以,但模型方法不是执行此操作的正确方法。模型上的方法用于单个实例上的操作。如果您想在整个 QuerySet 上做某事,请改用 ORM Manager。

如果有经理,这将如下所示:

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):

Contestant.objects.filter(...)

...total_points 字段将“神奇地”用于每个对象。

【讨论】:

+1。谢谢,我也大致沿着这些思路思考。我需要检查我们是否真的到处都需要它。让我们等待是否有人提出任何替代方案,但这似乎是我目前所知道的最明智的方法。 请注意,我们最后使用了github.com/initcrash/django-denorm(出于性能原因),但我接受了它,因为它是一种有效的方法。谢谢,伙计! 太棒了,这可行,但只使用 get_query_set 而不是 get_queryset @JasonSack 在 Django 1.6 中更改了名称。您可能使用 Django 1.5 或更早版本。

以上是关于如何在 Django 中预取聚合的@property?的主要内容,如果未能解决你的问题,请参考以下文章

教义 orm:绕过延迟加载并在 getter 中预取相关记录

如何根据先前的用户选择在 Django 中预填充(多个)ChoiceField

Django 查询集。如何只预取唯一的?

核心数据预取和 KVO 合规性

实现搜索的不同方式

Django预取相关并存在