Django按两个字段排序项目,但如果它们为零则忽略它们

Posted

技术标签:

【中文标题】Django按两个字段排序项目,但如果它们为零则忽略它们【英文标题】:Django order items by two fields, but ignoring them if they're zero 【发布时间】:2011-08-25 11:11:26 【问题描述】:

我有以下模型(为了这个问题的目的大大简化了):

class Product(models.Model):
    price = models.DecimalField(max_digits=8, decimal_places=2)
    sale_price = models.DecimalField(max_digits=10, blank=True, null=True, decimal_places=2)

对于大多数产品,price 将被填写,但 sale_price 不会。所以,我可以像这样按价格订购产品:

Product.objects.order_by('price')
Product.objects.order_by('-price')

但是,有些产品会有 sale_price,我无法找到一种方法来整齐地订购这些产品,以便销售价格与正常价格交织在一起。如果我尝试按两个字段排序:

Product.objects.order_by('sale_price','price')

...然后所有未在售的产品一起出现,然后是所有在售的产品,而不是交错价格。

这有意义吗?有没有人有办法解决这个问题?

谢谢!

【问题讨论】:

【参考方案1】:

您可以使用 extra() QuerySet 方法在 SQL 中使用 COALESCE 函数在查询中创建一个额外字段,该函数返回它传递的第一个非 NULL 值。

Product.objects.extra(select="current_price":"COALESCE(sale_price, price)", order_by=["-current_price"])

你必须将你的 order_by 放在 extra() 调用中,因为就 ORM 的其余部分而言,额外的手动字段“并不真正存在”,但语法与普通 Django order_by() 相同秒。

在此处查看 extra() 文档:http://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra

【讨论】:

太完美了,谢谢!可惜我不得不使用额外的,但如果 ORM 不能处理它,那么就没有其他选择了!谢谢。 在必要的时候不要害怕使用 extra(),在数据库级别做这种事情比在你的 Python 代码中进行黑客攻击要快得多,而且可能更清晰。唯一要记住的一点是坚持标准 SQL。例如,使用 ANSI SQL 中的 COALESCE,而不是使用 Oracle 扩展的 NVL。只要你这样做,extra() 是完全可移植的。像您这样的问题,涉及可移植性问题,就是为什么添加 extra() 作为原始 SQL 的替代方案。 它将sales_priceprice 的类型从十进制字段更改为字符串/unicode。就像 current_price 现在将是一个 unicode 字符串,而不是一个十进制字段。如何处理? 这种 COALESCE 方法的替代方案是什么?【参考方案2】:

如果您偶然发现了这个要求并且碰巧使用的是 Django 1.8 及更高版本,您可以使用django.db.models.functions.Coalesce 来获得更好的跨数据库引擎解决方案:

from django.db.models.functions import Coalesce

Product.objects.annotate(
    current_price=Coalesce('sale_price', 'price')
).order_by('-current_price')

【讨论】:

是否也可以将此排序作为模型上的默认排序? 此外,它似乎不适用于 Charfields。我有两个字符域“名称”和“昵称”。昵称通常为空。如果我这样做:.annotate(current_name=Coalesce('name', 'nickname')).order_by('current_name')。排序看起来不错,但是如果您有不同长度的名称(ak 'test' 和 'testj'),具有相同的昵称('testnickname'),以及其他一些只有 'test' 而没有昵称的对象,它会显示为:'testnickname', 'test', 'test', 'testnickname'(我的 str 方法检查是否为 testnickname,然后显示,否则只显示名称)。 啊,我发现了问题。我没有空字段,但我有 '' 字段(根据 Django 文档的建议)。那么 Coalesce 就不能很好地工作了。 Django ORM 将简单地在数据库上执行 COALESCE,因此您看到的行为很可能是数据库上函数的正确行为。例如在解决方案上考虑this answer。

以上是关于Django按两个字段排序项目,但如果它们为零则忽略它们的主要内容,如果未能解决你的问题,请参考以下文章

Django 按日期排序,但最后有“无”?

在 django(postgres 后端)中按数字字符串排序查询结果

如何在 django admin change_form 中更改外键字段的顺序

一个 If 语句,用于查找数字字段是不是留空或有太多小数位,但如果小数位只有零则忽略

核心日期一对一排序

Django模板按“不相关”模型的字段排序