logo

Сортировка моделей Django по NULLS LAST в Postgresql

Быстрый ответ

Для добавления "NULLS LAST" в запросы Django можно использовать аргумент nulls_last применительно к функции order_by(), и совместно использовать его с F() выражением. Так, чтобы упорядочить поля my_field в модели MyModel так, чтобы NULL-значения располагались последними, следует использовать данную конструкцию:

Python
Скопировать код
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 определённым значением и тем самым переместить такие записи в конец списка при сортировке:

Python
Скопировать код
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 запрос:

SQL
Скопировать код
SELECT * FROM parcels ORDER BY weight ASC;

Посылки с неизвестным весом (📦❓) могут оказаться в любом месте после сортировки:

До использования "NULLS LAST": [Легкая 📦, 📦, 📦❓, 📦, Тяжелая 📦]

При использовании "NULLS LAST" в запросе:

SQL
Скопировать код
SELECT * FROM parcels ORDER BY weight ASC NULLS LAST;

Посылки без указания веса (📦❓) будут расположены в конце:

После использования "NULLS LAST": [Легкая 📦, 📦, 📦, Тяжелая 📦, 📦❓❓]

Django "из коробки" не поддерживает NULLS LAST, тем не менее, с помощью некоторой настройки:

Python
Скопировать код
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:

Python
Скопировать код
# Сортировка: сначала по '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 и её безопасными дополнениями.

Полезные материалы

  1. Query Expressions | Документация Django | Django — официальное руководство по использованию F-выражений, включая NULLS LAST.
  2. Conditional Expressions | Документация Django | Django — информация по условным выражениям и применению NULLS LAST.
  3. Database Functions | Документация Django | Django — обзор функций Django для формирования порядка сортировки.
  4. Aggregation | Документация Django | Django — описание принципов агрегации в Django.
  5. PostgreSQL: Документация: 16: 7.5. Сортировка строк (ORDER BY) — подробная информация о NULLS FIRST и NULLS LAST в документации PostgreSQL.