Ответ 1
Вы можете использовать аннотацию() из django agrregation, чтобы сделать трюк:
items = Item.objects.all().annotate(null_position=Count('position')).order_by('-null_position', 'position')
У меня проблема с упорядочением запросов django.
Моя модель содержит поле с именем position
(PositiveSmallIntegerField), которое я хотел бы использовать для запроса результатов запроса.
Я использую order_by('position')
, который отлично работает.
Проблема: поле my position
имеет значение NULL (null=True, blank=True
), потому что я не хочу указывать позицию для каждых 50000 экземпляров моей модели: (
Когда некоторые экземпляры имеют NULL-позицию, order_by
возвращает их в верхней части списка: я бы хотел, чтобы они были в конце...
В RAW SQL я писал такие вещи, как "IF(position IS NULL or position='', 1, 0)
" (см. http://www.shawnolson.net/a/730/mysql-sort-order-with-null.html): можно ли получить тот же результат с помощью Django, без написания исходного SQL?
Большое спасибо!
Вы можете использовать аннотацию() из django agrregation, чтобы сделать трюк:
items = Item.objects.all().annotate(null_position=Count('position')).order_by('-null_position', 'position')
По состоянию на Django 1.8 вы можете использовать Coalesce()
для преобразования NULL
в 0
.
Пример:
import datetime
from django.db.models.functions import Coalesce, Value
from app import models
# Coalesce works by taking the first non-null value. So we give it
# a date far before any non-null values of last_active. Then it will
# naturally sort behind instances of Box with a non-null last_active value.
the_past = datetime.datetime.now() - datetime.timedelta(days=10*365)
boxes = models.Box.objects.all().annotate(
new_last_active=Coalesce(
'last_active', Value(the_past)
)
).order_by('-new_last_active')
Использование extra(), как сказал Игнасио, оптимизирует конечный запрос. В моем приложении я сохранил более 500 мс (что много для запроса) в обработке базы данных, используя extra() вместо annotate()
Вот как это выглядело бы в вашем случае:
items = Item.objects.all().extra(
'select': {
'null_position': 'CASE WHEN {tablename}.position IS NULL THEN 0 ELSE 1 END'
}
).order_by('-null_position', 'position')
{tablename} должно быть чем-то вроде {Item app} _item следующего имени таблицы django по умолчанию.
Я обнаружил, что синтаксис ответа Pablo должен быть обновлен до следующего в моей установке 1.7.1:
items = Item.objects.all().extra(select={'null_position': 'CASE WHEN {name of Item table}.position IS NULL THEN 0 ELSE 1 END'}).order_by('-null_position', 'position')
QuerySet.extra()
можно использовать для ввода выражений в запрос и упорядочения ими.