11

我在我的 Django 应用程序中使用以下代码:

pictures = gallery.picture_set.annotate( score=models.Sum( 'picturevote__value' ) ).order_by( '-score' )

有一张画廊的桌子。他们每个人都有一些图片。当用户对图片投赞成票或反对票时,将插入“picturevote”中的新行并将其连接到图片。然后我可以得到图片的总分。现在我想按它们的分值排序一个画廊的图片。但是由于表连接,当根本没有投票时,得分的值可能为 NULL。尽管如此,'NULL' 的分数应被视为'0'。

有任何想法吗?

编辑:好的,这里有一些额外的解释:问题是上面示例中的聚合设置score为NULL。当我想显示分数时,我使用这样的东西:

score = self.picturevote_set.aggregate( models.Sum( 'value' ) )[ 'value__sum' ] or 0

然后聚合导致 NULL(如果没有图片投票行)或某个值。如果为 NULL,or 表达式将其转换为可显示的整数值。但这只是解决了由 NULL 值引起的显示问题。当我想按照score第一个代码示例中的这个值对图片进行排序时,所有带有 NULL 的条目都放在有序结果集的末尾。首先是评分为正的图片,然后是评分为负的图片,然后是到目前为止没有投票赞成或反对的图片,因为它们的值为 NULL score

我的问题是如何改变这种行为以使顺序正确。

4

3 回答 3

20

从 Django 1.8 开始,有一个Coalesce数据库功能。您的查询可能如下所示:

from django.db.models.functions import Coalesce    

score = self.picturevote_set.aggregate(Coalesce(models.Sum('value'), 0))
于 2015-09-23T07:03:28.477 回答
15

在引入注释之前,您可能曾经extra做过这样的事情,我认为在没有投票的情况下应该返回0(如果它不用于任何特定的数据库实现,您至少可以直接插入必要的COALESCE函数调用 - COALESCE(SUM(value), 0)- 使用此方法):

pictures = gallery.picture_set.extra(
    select={
        'score': 'SELECT SUM(value) FROM yourapp_picturevote WHERE yourapp_picturevote.picture_id = yourapp_picture.id',
    },
    order_by=['-score']
)

我看不到任何将您自己的 SQL 添加到新注释内容(我还没有亲自使用过)的内置方法,但看起来您应该能够像这样创建一个新注释:

from django.db.models import aggregates
from django.db.models.sql import aggregates as sql_aggregates

class SumWithDefault(aggregates.Aggregate):
    name = 'SumWithDefault'

class SQLSumWithDefault(sql_aggregates.Sum):
    sql_template = 'COALESCE(%(function)s(%(field)s), %(default)s)'

setattr(sql_aggregates, 'SumWithDefault', SQLSumWithDefault)

这看起来相当难看,因为由于查找 SQL 聚合类的方式,您需要将新聚合修补到django.db.models.sql.aggregates其中,但我们在这里所做的只是添加一个新聚合,其中子类Sum,硬编码对COALESCE函数的调用并添加占位符对于默认值,您必须将其作为关键字参数提供(至少在这个非常基本的示例实现中)。

这应该让您执行以下操作:

pictures = gallery.picture_set.annotate(score=SumWithDefault('picturevote__value', default=0).order_by('-score')
于 2009-02-17T11:30:34.553 回答
0

根据此问题,您现在可以执行以下操作:

score = self.picturevote_set.aggregate((models.Sum('value', default=0))

如果 sum 的结果为 None 类型,则返回默认值。在这种情况下,数字 0。

注意:它是在 Django V4 中发布的

于 2022-02-22T14:05:31.187 回答