在 Django 模型中将 NULL 视为“0”
Posted
技术标签:
【中文标题】在 Django 模型中将 NULL 视为“0”【英文标题】:Treat NULL as '0' in Django model 【发布时间】:2010-10-07 20:55:02 【问题描述】:我在我的 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
。
我的问题是如何改变这种行为以使顺序正确。
【问题讨论】:
您能否更清楚地说明问题所在?如果没有投票,你会从上面的代码中得到错误吗?什么错误? 【参考方案1】:从 Django 1.8 开始,有一个 Coalesce
数据库函数。您的查询可能如下所示:
from django.db.models.functions import Coalesce
score = self.picturevote_set.aggregate(Coalesce(models.Sum('value'), 0))
【讨论】:
还有开放功能请求,Django核心开发者评论:code.djangoproject.com/ticket/10929#comment:16 因为“功能请求”已经存在 12 年了......我认为 Coalesce 是最好的解决方法【参考方案2】:在引入注释之前,您可能已经使用 extra
来执行类似的操作,我认为在没有投票的情况下应该返回 0
(如果没有t 对于任何特定的数据库实现,您至少可以直接插入必要的 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')
【讨论】:
你好。这是在很长一段时间之后,但是我认为您的 sql_template 中有错误。你必须这样写: sql_template = '%(function)s(COALESCE(%(field)s, %(default)s))' 。请考虑一下并告诉我你是否同意 - 这已经困扰我好几个月了! django1.8 now supportsCoalesce
作为数据库函数以上是关于在 Django 模型中将 NULL 视为“0”的主要内容,如果未能解决你的问题,请参考以下文章
在 django 3.0 中将复选框选项显示为“已禁用”。模型形式