I want to do a 3 lvl prefetch_related but I can't make it work.
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
And on my Serializers.py I call it like that:
json['name'] = answer.question.orderedquestion.select_set.get(id=i).name
I don't know if this metters but my OrderedQuestion class inherits from Question.
My Models.py
class Answer(models.Model):
updated_datetime = models.DateTimeField(default=timezone.now)
profile = models.ForeignKey(Profile, related_name='answer', on_delete=models.CASCADE)
question = models.ForeignKey(Question, related_name='answer', on_delete=models.CASCADE)
order = JSONField(blank=True, null=True)
class Question(models.Model):
objects = InheritanceManager()
title_question = models.CharField(max_length=256, unique=True)
title_interest = models.CharField(max_length=256)
description_question = models.CharField(max_length=2048, null=True, blank=True)
description_interest = models.CharField(max_length=2048, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
class OrderedQuestion(Question):
multiple_choice = models.BooleanField(default=False)
dont_care = models.BooleanField(default=False)
class Select(models.Model):
name = models.CharField(max_length=1024)
choice_question = models.ForeignKey(ChoiceQuestion, on_delete=models.CASCADE, null=True, blank=True)
ordered_question = models.ForeignKey(OrderedQuestion, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return str(self.id) ' - ' self.name
How can I fetch select_set on the first query so my serializer stop doing N queries for each object?
Thank you so much for the help.
CodePudding user response:
I think your code should work. I wrote year ago similar solution for real state agency and it looks like that:
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')
Debugging with django-debug-toolbar
package is showing that this code does only 3 queries. The 3 queries are must, because this is way how SQL works - it contains 3 different tables, it can't fetch data from multiple tables using one query, but it's probably better than 100 queries without using .prefetch_related()
or .select_related()
.
You can check if your code works using mentioned django-debug-toolbar
or by using django-silk
.
@EDIT: Sorry, I was dumb - just noticed that you are using this line:
json['name'] = answer.question.orderedquestion.select_set.get(id=i).name
You can't fetch question by using direct-attribute call, you must do something like this:
answer.question_set.first().orderedquestion_set.first().select_set_set.get(id=i).name
The many-to-many
and reverse-fk
relations contains MULTIPLE objects. You can't fetch just single question without using .first()
or calling first element by:
answer.question_list[0]
(If you use to_attr
attribute in prefetch).
Also, if you use direct models.ForeignKey
call, you should use select_related()
, not prefetch_related()
.