Django Rest Framework - отображение дочернего значения на родительском сериализаторе без лишних запросов
У меня есть две модели:
class Parent(models.Model):
name = models.CharField(max_length=255)
level = models.IntegerField()
class Child(models.Model):
parent = models.ForeignKey(Parent, related_name='children')
therefore_name = models.CharField(max_length=255)
level = models.IntegerField()
active_from = models.DateTimeField(auto_now_add=True)
active_to = models.DateTimeField(null=True, blank=True)
Детская модель используется для того, чтобы иметь возможность "перезаписывать" значения на родительской, и существует валидация для предотвращения родителя с несколькими перекрывающимися детьми с одинаковыми датами active_from и active_to.
Мое мнение:
class FacilityViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = ParentSerializer
def get_queryset(self):
now = timezone.now()
parents = Parent.objects.all().prefetch_related(
Prefetch('children', queryset=Child.objects.exclude(valid_from__gt=now, valid_from__isnull=False).exclude(valid_to__lt=now, valid_to__isnull=False).distinct())
)
return parents
Мой сериализатор:
class ParentSerializer(serializers.ModelSerializer):
class Meta:
model = Parent
fields = ['id']
def to_representation(self, instance):
representation = super().to_representation(instance)
if hasattr(instance, 'children') and instance.children.exists():
child = instance.children.first()
else:
child = Child(therefore_name=instance.name, level=instance.level)
representation['name'] = child.therefore_name
representation['level'] = child.level
return representation
Это работает, но код делает много дополнительных запросов. Есть ли что-то, что я могу сделать, чтобы сократить количество запросов?
Вот код, который делает дополнительные запросы:
if hasattr(instance, 'children') and instance.children.exists():
child = instance.children.first()
В ParentSerializer.to_representation()
нет необходимости сначала вызывать instance.children.exists()
. Просто вызовите instance.children.first()
, и если дочернего элемента не существует, это будет None
.
В базу данных уже попали при предварительной выборке, так что это не запрашивает ничего лишнего:
child = None
for child in instance.children.all()[:1]:
child = child
if not child:
child = Child(therefore_name=instance.name, level=instance.level)