Фильтр с порядком для двух полей
У меня есть модель Klass с полями следующего содержания:
date_start = models.DateField(null=True, blank=True, default=date.today)
date_finish = models.DateField(null=True, blank=True)
Как видите, дата_начало будет обычно заполнена, а дата_завершение - нет. Если ни одна из них не заполнена, мы не должны рассматривать эту запись при дальнейшей фильтрации.
Я хотел бы видеть объекты (первые N результатов) упорядоченные по самой поздней дате независимо от того, дата_начала или дата_окончания. Точнее: рассматриваемая дата должна быть дата_финиша, если она существует, дата_начала в противном случае.
Обратите внимание, что я не хочу, чтобы N/2 законченных элементов и N/2 только начатых элементов были конкатенированы, а последние N "тронутых" элементов.
Моя первая идея - предоставить дополнительное поле considered_date, которое будет заполняться так, как я предложил, но я не знаю, как это реализовать. Должно ли это быть сделано:
- на уровне модели, так что новое поле DateField должно быть добавлено и его содержимое всегда заполнено чем-л .
- отбирается для 2 отдельных условий (N элементов в каждом), затем предоставляется временное дополнительное поле (но без сохранения в db), затем 2 набора объединяются и снова упорядочиваются для этого нового условия
Забавный факт: у меня также есть BooleanField, которое указывает, закрыт период или нет. Оно нужно мне для простоты и фильтрации, но мы можем использовать его и здесь. Очевидно, оно обрабатывается функцией save() (по умолчанию True, устанавливается в False, если заполняется date_finish).
Честно говоря, эта "функция" не является критической в моем приложении. Она просто отображает некоторые "последние изменения" на странице приветствия, поэтому может срабатывать довольно часто.
Кажется, что ваше описание considered_date
является идеальным случаем использования COALESCE
sql функции, которая возвращает первое не NULL
значение из своих аргументов.
Итак, в обычном SQL это вернет значение date_finish
, если оно не NULL
или date_start
в противном случае (предполагая, что date_start
никогда не будет NULL
, поскольку оно имеет значение по умолчанию)
COALESCE(your_table_name.date_finish, your_table_name.date_start);
Django ORM имеет API для этой функции. Используя его с методом annotate
queryset, мы можем построить нужный запрос без создания дополнительных полей в модели.
Для удобства назовем вашу модель TestModel
from django.db.models.functions import Coalesce
TestModel.objects.annotate(
latest_touched_date=Coalesce("date_finish", "date_start")
).order_by("-latest_touched_date")
Обратите внимание, это будет работать только в том случае, если date_finish
больше date_start
. (что, как мне кажется, верно)
Вы можете добавить exclude
для фильтрации любых записей, где и date_finish
и date_start
являются NULL
s
from django.db.models.functions import Coalesce
TestModel.objects.exclude(date_start__isnull=True, date_finish__isnull=True).annotate(
latest_touched_date=Coalesce("date_finish", "date_start")
).order_by("-latest_touched_date")
Просто нарежьте набор запросов, чтобы получить первые N
результаты.