Подтвердить что ты не робот

Django: порядок по позиции, игнорирующий NULL

У меня проблема с упорядочением запросов 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?

Большое спасибо!

4b9b3361

Ответ 1

Вы можете использовать аннотацию() из django agrregation, чтобы сделать трюк:

items = Item.objects.all().annotate(null_position=Count('position')).order_by('-null_position', 'position')

Ответ 2

По состоянию на 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')

Ответ 3

Использование 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 по умолчанию.

Ответ 4

Я обнаружил, что синтаксис ответа 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')

Ответ 5

QuerySet.extra() можно использовать для ввода выражений в запрос и упорядочения ими.