Django 4.x - Условный порядок_бытия набора запросов
Цель
Задача состоит в том, чтобы условно упорядочить QuerySet по одному из трех различных полей даты в представлении на основе другого поля в модели. Поскольку условное упорядочивание не может быть выполнено с помощью Class Meta, я изучаю возможность выполнения этой задачи в представлении.
Вот соответствующий отрывок из models.py
:
READING_PROGRESS = [
('---', '---'),
('1) On Reading List', '1) On Reading List'),
('2) Reading In Progress', '2) Reading In Progress'),
('3) Completed Reading', '3) Completed Reading'),
]
class ReadingProgress(models.Model):
record = models.ForeignKey(
LibraryRecord,
related_name='record_in_reading_progress',
on_delete=models.CASCADE,
null=True,
blank=True,
verbose_name='Library record'
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=True,
blank=True
)
reading_progress = models.CharField(
max_length=30,
choices=READING_PROGRESS,
default='---'
)
date_added = models.DateField(
auto_now=False,
auto_now_add=False,
null=True,
blank=True,
)
date_started = models.DateField(
auto_now=False,
auto_now_add=False,
null=True,
blank=True,
)
date_completed = models.DateField(
auto_now=False,
auto_now_add=False,
null=True,
blank=True,
)
class Meta:
ordering = [
'reading_progress',
]
verbose_name_plural = 'Reading Progress'
unique_together = ('record', 'user',)
# Record metadata
date_created = models.DateTimeField(
auto_now_add=True
)
def __str__(self):
return f'{self.record.title} - {self.reading_progress}'
Соответствующими полями в модели являются:
reading_progress
date_added
date_started
date_completed
Каждое поле даты соответствует значению статуса. Я хочу иметь возможность упорядочить набор QuerySet в представлении по полю reading_progress
:
- Когда
reading_progress
== '1) В списке для чтения', тогда упорядочить поdate_added
- Когда
reading_progress
== '2) В процессе чтения', то порядок поdate_started
- Когда
reading_progress
== '3) Законченное чтение', тогда порядок поdate_completed
Исследование перед ответом
Я провел некоторое исследование и нашел полезный на вид API QuerySet под названием annotate()
. Похоже, это то, что нужно (Django docs).
В документации Django, кажется, говорится, что:
- я мог бы фильтровать
- и затем аннотировать отфильтрованный QuerySet .
- После дополнительных исследований я пришел к выводу, что могу использовать
F()
как способ реализации запроса в APIannotate()
Ответ
Оказывается, при использовании Case нужно обязательно следовать документации и импортировать то, что необходимо. Спасибо поставщику ответа, отмеченного как правильный! :)
Кроме того, я удалил output_field, поскольку существует только один возможный тип данных поля (DateTime) и на него не нужно явно ссылаться.
from django.db.models import Case, F, Q, Value, When
reading_progress = ReadingProgress.objects.filter(user__username=self.request.user)\
.annotate(
date_to_display=Case(
When(reading_progress='1) On Reading List', then=F('date_added')),
When(reading_progress='2) Reading In Progress', then=F('date_started')),
When(reading_progress='3) Completed Reading', then=F('date_completed')),
)
)
Вы должны импортировать соответствующие части в пределах views.py
from django.db.models import Case, F, Q, Value, When