Django Rest Framework - display child value on parent serializer without making too many extra queries

I have this two models:

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)

Child model is used to be able to "overwrite" values on parent, and there is validation to prevent a parent with multiple overlapping children with the same active_from and active_to dates.

My view:

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

My serializer:

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

This works, but the code makes alot of extra queries. Is there something I could do to cut the queries down?

This is the code that is makes extra queries:

if hasattr(instance, 'children') and instance.children.exists():
  child = instance.children.first()

In the ParentSerializer.to_representation(), there is no need to call instance.children.exists() first. Just call instance.children.first() and if no child exists it will be None.

The database is already hit on prefetch, so this does not query anything extra:

child = None

for child in instance.children.all()[:1]:
  child = child

if not child:
  child = Child(therefore_name=instance.name, level=instance.level)
Back to Top