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)
Вернуться на верх