Django 4.x - Conditional QuerySet для пагинации и отношений "многие-ко-многим
Отказ от ответственности: Я искал, и вопрос, решающий эту конкретную задачу, не был найден на момент публикации.
Требование
Для представления на основе классов мне нужно реализовать пагинацию для набора запросов, полученного через отношения "многие ко многим". Вот требование с более конкретным описанием:
- Многие библиотечные записи могут принадлежать многим Коллекциям
- Для большинства (но не обязательно всех) Коллекций требуются веб-страницы, поэтому мне нужно создавать представления/шаблоны/урлы на основе того, что клиент определяет как необходимое
- Каждая страница коллекции, отображающая соответствующие записи библиотеки, требует пагинации, так как может быть 100 записей для отображения.
Первый подход
Поэтому, учитывая это требование, я подошел к делу так, как обычно подхожу к созданию CBV с пагинацией. Однако такой подход не позволил мне выполнить требование. Я быстро обнаружил, что метод Pagination в CBV создавал объект на основе объявленной модели, но связь "многие ко многим" не работала.
Я исследовал использование объекта в шаблоне, но после нескольких попыток у меня ничего не получилось. Мне нужно отобразить объекты записей библиотеки, но отношения "многие ко многим" требуют, чтобы я сделал это после определения записей на основе коллекции, к которой они принадлежат.
Надеюсь, что этот вопрос поможет кому-нибудь еще. Если, читая следующий подход, вы сможете придумать, как рефакторить/оптимизировать, я буду рад узнать. Примечание: я намеренно не реализовал Pythonic List Comprehension для моего личного предпочтения читабельности.
В итоге я добавил get_queryset()
к:
- Запросите коллекцию на предмет принадлежащих ей записей, чтобы затем
- Создать список идентификаторов записей, чтобы затем
- Вернуть QuerySet, отфильтровав для
pk__in
(пк существует в списке library_record_ids)
Вот полученный код.
def get_queryset(self):
library_record_ids = []
library_collection = CollectionOrder.objects.filter(
collection__collection='<Collection Name>'
).order_by('order_number')
for record in library_collection:
library_record_ids.append(record.record.id)
return LibraryRecord.objects.filter(pk__in=library_record_ids)
Требование было выполнено. Я приветствую конструктивную критику. Мое намерение поделиться этим Q&A состоит в том, чтобы попытаться немного отблагодарить сообщество Stack Overflow, которое так хорошо послужило мне с начала этого путешествия в Django.
Пожалуйста, неработайте с record.record.id
: это будет каждый раз делать запрос для каждого CollectionOrder
объекта, и таким образом, если есть 100 CollectionOrder
объектов, это сделает 100 дополнительных запросов, и таким образом, в конечном итоге сделает 102 запроса. Таким образом, если количество совпадений будет достаточно большим, то в конечном итоге он перестанет отвечать (в пределах разумного времени)
Более того, pk__in=library_records_ids
будет не соблюдать порядок library_record_ids
. Действительно, он может возвращать LibraryRecord
в любом порядке, если только они имеют первичные ключи, которые являются членами списка.
Вы можете сделать запрос с помощью:
def get_queryset(self):
return LibraryRecord.objects.filter(
collectionorder__collection__collection='collection-name'
).order_by('collectionorder__order_number')
Where collectionorder
is the related_query_name=…
[Django-doc] for the ForeignKey
, OneToOneField
or ManyToManyField
named record
from CollectionOrder
to the LibraryRecord
model. If you did not specify a value for the related_query_name=…
parameter, it will take the value for the related_name=…
parameter [Django-doc], and if you did not specify that one either, it will use the name of the source model (so where the relation is defined) in lowercase, so in this case collectionorder
.
Таким образом, будет соблюдаться collectionorder__order_number
как условие упорядочивания, и выполняться в одном запросе к базе данных, минимизируя количество запросов к базе данных.