Элементы заказа Django по двум полям, но игнорируя их, если они равны нулю
У меня есть следующая модель (очень упрощенная для целей этого вопроса):
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)
Для большинства продуктов цена будет заполнена, а sale_price не будет. Итак, я могу заказать продукты по цене так:
Product.objects.order_by('price')
Product.objects.order_by('-price')
Однако некоторые продукты будут иметь sale_price, и я не могу найти способ упорядочить их аккуратно, чтобы цена продажи чередовалась с обычной ценой. Если я попробую упорядочить оба поля:
Product.objects.order_by('sale_price','price')
... тогда все продукты, которые не продаются, появляются вместе, за ними следуют все, что есть, вместо того, чтобы чередовать цены.
Это имеет смысл? У кого-нибудь есть способ решить это?
Спасибо!
Ответы
Ответ 1
Вы можете использовать метод extra() QuerySet для создания дополнительного поля в вашем запросе с помощью функции COALESCE в SQL, которая возвращает первое значение, отличное от NULL, которое оно передало.
Product.objects.extra(select={"current_price":"COALESCE(sale_price, price)"}, order_by=["-current_price"])
Вы должны поместить свой order_by в вызов extra(), поскольку дополнительное поле вручную "на самом деле не существует" по отношению к остальной части ORM, но синтаксис такой же, как обычный Django order_by() с.
Смотрите дополнительную() документацию здесь: http://docs.djangoproject.com/en/1.3/ref/models/querysets/#extra
Ответ 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')