Django REST Serializer выполняет N + 1 вызов базы данных для нескольких вложенных отношений, 3 уровня
У меня есть ситуация, когда моя модель имеет отношение внешнего ключа:
# models.py
class Child(models.Model):
parent = models.ForeignKey(Parent,)
class Parent(models.Model):
pass
и мой сериализатор:
class ParentSerializer(serializer.ModelSerializer):
child = serializers.SerializerMethodField('get_children_ordered')
def get_children_ordered(self, parent):
queryset = Child.objects.filter(parent=parent).select_related('parent')
serialized_data = ChildSerializer(queryset, many=True, read_only=True, context=self.context)
return serialized_data.data
class Meta:
model = Parent
Когда я вызываю Parent в своих представлениях для N number of Parents, Django делает N вызовов базы данных внутри сериализатора, когда он захватывает дочерние элементы. Есть ли способ получить ВСЕХ детей для ВСЕХ Родителей, чтобы минимизировать количество вызовов базы данных?
Я пробовал это, но это не решает мою проблему:
class ParentList(generics.ListAPIView):
def get_queryset(self):
queryset = Parent.objects.prefetch_related('child')
return queryset
serializer_class = ParentSerializer
permission_classes = (permissions.IsAuthenticated,)
РЕДАКТИРОВАТЬ
Я обновил код ниже, чтобы отразить обратную связь Алекса .... которая решает N + 1 для одного вложенного отношения.
# serializer.py
class ParentSerializer(serializer.ModelSerializer):
child = serializers.SerializerMethodField('get_children_ordered')
def get_children_ordered(self, parent):
# The all() call should hit the cache
serialized_data = ChildSerializer(parent.child.all(), many=True, read_only=True, context=self.context)
return serialized_data.data
class Meta:
model = Parent
# views.py
class ParentList(generics.ListAPIView):
def get_queryset(self):
children = Prefetch('child', queryset=Child.objects.select_related('parent'))
queryset = Parent.objects.prefetch_related(children)
return queryset
serializer_class = ParentSerializer
permission_classes = (permissions.IsAuthenticated,)
Теперь скажем, у меня есть еще одна модель, которая является внуком:
# models.py
class GrandChild(models.Model):
parent = models.ForeignKey(Child,)
class Child(models.Model):
parent = models.ForeignKey(Parent,)
class Parent(models.Model):
pass
Если я размещу следующее в моемviews.py
для родителейqueryset
:
queryset = Parent.objects.prefetch_related(children, 'children__grandchildren')
Не похоже, что этих внуков переносят в ChildSerializer, и, таким образом, я снова запускаю еще одну проблему N + 1. Есть мысли по этому поводу?
РЕДАКТИРОВАТЬ 2
Возможно, это обеспечит ясность ... Возможно, причина, по которой я все еще сталкиваюсь с вызовами из базы данных N + 1, заключается в том, что и мои дети, и классы внуков являются полиморфными .... т.е.
# models.py
class GrandChild(PolymorphicModel):
child = models.ForeignKey(Child,)
class GrandSon(GrandChild):
pass
class GrandDaughter(GrandChild):
pass
class Child(PolymorphicModel):
parent = models.ForeignKey(Parent,)
class Son(Child):
pass
class Daughter(Child):
pass
class Parent(models.Model):
pass
и мои сериализаторы выглядят больше так:
# serializer.py
class ChildSerializer(serializer.ModelSerializer):
grandchild = serializers.SerializerMethodField('get_children_ordered')
def to_representation(self, value):
if isinstance(value, Son):
return SonSerializer(value, context=self.context).to_representation(value)
if isinstance(value, Daughter):
return DaughterSerializer(value, context=self.context).to_representation(value)
class Meta:
model = Child
class ParentSerializer(serializer.ModelSerializer):
child = serializers.SerializerMethodField('get_children_ordered')
def get_children_ordered(self, parent):
queryset = Child.objects.filter(parent=parent).select_related('parent')
serialized_data = ChildSerializer(queryset, many=True, read_only=True, context=self.context)
return serialized_data.data
class Meta:
model = Parent
Плюс то же самое для внучки, внук, я сэкономлю вам детали в коде, но я думаю, вы понимаете.
Когда я запускаю свое представление для ParentList и отслеживаю запросы к БД, я получаю что-то вроде тысячных запросов только для нескольких родителей.
Если я запускаю один и тот же код в оболочке django, я могу выполнить один и тот же запрос не более чем в 25 запросах. Я подозреваю, что это может быть связано с тем, что я использую django-polymorphic библиотеку? Причина в том, что есть таблица базы данных Child и GrandChild, в дополнение к каждой таблице Son / Daughter, Grandson / Granddaughter, всего 6 таблиц. через эти объекты. Так что моя интуиция говорит мне, что я скучаю по этим полиморфным таблицам.
Или, может быть, есть более элегантное решение для моей модели Daata?