Django prefetch_related вложенные модели
Я хочу сделать 3 lvl prefetch_related, но не могу заставить его работать.
views.py:
queryset = obj.answer.all().prefetch_related(Prefetch('question ', queryset=Question.objects.prefetch_related(Prefetch('orderedquestion', queryset=OrderedQuestion.objects.prefetch_related('select_set')))))
return AnswerSerializer(queryset, many=True).data
А в моем Serializers.py я вызываю его так:
json['name'] = answer.question.orderedquestion.select_set.get(id=i).name
Не знаю, соответствует ли это действительности, но мой класс OrderedQuestion наследует от Question.
Как я могу получить select_set в первом запросе, чтобы мой сериализатор перестал делать N запросов для каждого объекта?
Большое спасибо за помощь.
Я думаю, что ваш код должен работать. Я написал год назад похожее решение для реального государственного агентства и оно выглядит так:
OfferType.objects.prefetch_related(
Prefetch('offer_type_biurowin', queryset=OfferTypeBiurowin.objects.prefetch_related(
Prefetch('offers', queryset=Offer.objects.select_related('locality').prefetch_related(
Prefetch('gallery', to_attr='gallery_list')), to_attr='offer_list')
), to_attr='biurowin_list')
Отладка с помощью пакета django-debug-toolbar
показывает, что этот код выполняет только 3 запроса. Эти 3 запроса необходимы, потому что так работает SQL - он содержит 3 различные таблицы, он не может получить данные из нескольких таблиц с помощью одного запроса, но это, вероятно, лучше, чем 100+ запросов без использования .prefetch_related()
или .select_related()
.
Вы можете проверить, работает ли ваш код, используя упомянутые django-debug-toolbar
или django-silk
.
@EDIT: Извините, я был туп - только что заметил, что вы используете эту строку:
json['name'] = answer.question.orderedquestion.select_set.get(id=i).name
Вы не можете получить вопрос, используя прямой вызов атрибута, вы должны сделать что-то вроде этого:
answer.question_set.first().orderedquestion_set.first().select_set_set.get(id=i).name
Отношения many-to-many
и reverse-fk
содержат МНОЖЕСТВО объектов. Вы не можете получить только один вопрос без использования .first()
или вызова первого элемента по:
answer.question_list[0]
(Если вы используете атрибут to_attr
в предварительной выборке).
Также, если вы используете прямой вызов models.ForeignKey
, вы должны использовать select_related()
, а не prefetch_related()
.