Сортировка моделей Django по NULLS LAST в Postgresql
Быстрый ответ
Для добавления "NULLS LAST" в запросы Django можно использовать аргумент nulls_last применительно к функции order_by(), и совместно использовать его с F() выражением. Так, чтобы упорядочить поля my_field в модели MyModel так, чтобы NULL-значения располагались последними, следует использовать данную конструкцию:
from django.db.models import F
# Упорядочиваем поля 'my_field' по убыванию с расположением значений NULL в конце
queryset = MyModel.objects.order_by(F('my_field').desc(nulls_last=True))
Такой код обеспечит упорядочивание my_field по убыванию, при этом NULL-значения будут всегда располагаться в конце списка.

Пошаговое руководство
Реализация "NULLS LAST" в более ранних версиях Django
Если вы используете версию Django старше 1.11, можно достичь поведения, аналогичного NULLS LAST, используя аннотации. Здесь следует применить функции БД, такие как Coalesce вместе с Value, чтобы заместить NULL определённым значением и тем самым переместить такие записи в конец списка при сортировке:
from django.db.models import Value, IntegerField
from django.db.models.functions import Coalesce
# Маркировка полей с NULL значениями большим числом для их отображения в конце списка
queryset = MyModel.objects.annotate(
my_field_order=Coalesce('my_field', Value(999999))
).order_by('my_field_order')
В этом случае, предположим, что my_field — это числовое поле. Мы назначаем максимально большое число полям с NULL, чтобы они всегда оказались в конце списка.
Индивидуальные решения для уникальных требований
Если у вас есть специальные требования, не бойтесь адаптировать Django под свои нужды. Рассмотрите возможность создания NullsLastSQLCompiler или NullsLastManager для своих моделей. Такой подход помимо реализации стратегии "NULLS LAST" упростит взаимодействие с сложными системами управления базами данных (СУБД), которые требуют детальной настройки SQL-запросов.
Мониторинг производительности
Настроив запросы, не забывайте следить за производительностью базы данных. Важно использовать методы select_related() и prefetch_related() для эффективной работы с связанными данными, чтобы уменьшить количество запросов к БД и ускорить время отклика.
Визуализация
Проясним приведенные понятия на наглядном примере ленты конвейера на упаковочной станции:
Конвейер в процессе работы: [📦, 📦, 📦, 📦, ❓]
Обычный SQL запрос:
SELECT * FROM parcels ORDER BY weight ASC;
Посылки с неизвестным весом (📦❓) могут оказаться в любом месте после сортировки:
До использования "NULLS LAST": [Легкая 📦, 📦, 📦❓, 📦, Тяжелая 📦]
При использовании "NULLS LAST" в запросе:
SELECT * FROM parcels ORDER BY weight ASC NULLS LAST;
Посылки без указания веса (📦❓) будут расположены в конце:
После использования "NULLS LAST": [Легкая 📦, 📦, 📦, Тяжелая 📦, 📦❓❓]
Django "из коробки" не поддерживает NULLS LAST, тем не менее, с помощью некоторой настройки:
from django.db.models import F, ExpressionWrapper, Value, IntegerField
# Упорядочиваем посылки
queryset = Model.objects.annotate(
weight_order=ExpressionWrapper(
F('weight'), output_field=IntegerField(), nulls_last=True
)
).order_by('weight_order')
Мы можем обеспечить такой порядок, при котором посылки с неизвестным весом всегда будут идти последними, а это упростит процесс упаковки.
Отладка и работа с различными СУБД
Составляя кастомные запросы, не забывайте просматривать логи, чтобы лучше понять сформированные SQL-запросы. Это будет полезным при отладке и адаптации к особенностям различных СУБД. Например, PostgreSQL хорошо поддерживает NULLS LAST, тогда как другие базы данных могут потребовать дополнительной настройки.
Дополнительный совет по упорядочиванию
При сложной сортировке сначала учитывайте поля с ненулевыми данными, а затем применяйте nulls_last:
# Сортировка: сначала по 'populated_field', затем по 'my_field' с NULL значениями в конце
queryset = MyModel.objects.order_by('populated_field', F('my_field').desc(nulls_last=True))
Таким образом, сортировка будет проводиться сначала по полю populated_field, а потом по my_field с "NULLS LAST".
Избегайте использование метода extra()
Метод extra() может создавать риск SQL-инъекций. Для предотвращения этого лучше пользоваться стандартными возможностями ORM Django и её безопасными дополнениями.
Полезные материалы
- Query Expressions | Документация Django | Django — официальное руководство по использованию
F-выражений, включаяNULLS LAST. - Conditional Expressions | Документация Django | Django — информация по условным выражениям и применению
NULLS LAST. - Database Functions | Документация Django | Django — обзор функций Django для формирования порядка сортировки.
- Aggregation | Документация Django | Django — описание принципов агрегации в Django.
- PostgreSQL: Документация: 16: 7.5. Сортировка строк (ORDER BY) — подробная информация о
NULLS FIRSTиNULLS LASTв документации PostgreSQL.