DRF Serializer POST method data prefetch

Hello, i’m struggling with one problem and i cant find the answer anywhere.
In our production setup we have models structure similar to this one (the models are much bigger, but its not important in the example) - please don’t mind model names they are purely exemplary

from django.db import models


class RelatedOnlyToMainModel(models.Model):
    field_1 = models.IntegerField()


class SmallestModel(models.Model):
    another_field = models.CharField(max_length=128)


class MainModel(models.Model):
    model_related_only_to_main_model = models.ManyToManyField(RelatedOnlyToMainModel, related_name='mains')
    smallests = models.ManyToManyField(SmallestModel, related_name='mains')
    one_of_many_fields = models.IntegerField()


class ModelA(models.Model):
    main = models.ForeignKey(MainModel, on_delete=models.SET_NULL, related_name='model_a', null=True)
    some_field = models.BooleanField()


class IntermediateModel(models.Model):
    model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE, related_name='small_one_connectors')
    smallest_model = models.ForeignKey(SmallestModel, on_delete=models.CASCADE, related_name='model_a_connectors')
    irrelevant_field = models.TextField()

Given following serializers that include nested serializers (presumably the root of all trouble):

from rest_framework import serializers

import main.models as models


class RelatedOnlyToMainModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.RelatedOnlyToMainModel
        fields = '__all__'


class IntermediateModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.IntermediateModel
        fields = '__all__'


class ModelASerializer(serializers.ModelSerializer):
    small_one_connectors = IntermediateModelSerializer(many=True)

    class Meta:
        model = models.ModelA
        fields = '__all__'

    def update(self, instance, validated_data):
        intermediate_data = validated_data.pop('small_one_connectors', [])
        intermediate_data_map = {str(intermediate.get('id')): intermediate for intermediate in intermediate_data}
        to_update = []
        for intermediate in instance.small_one_connectors.all():
            new_data = intermediate_data_map.get(intermediate.id)
            if new_data:
                for key in ['irrelevant_field']:
                    field_value = new_data.get(key)
                    if field_value:
                        setattr(intermediate, key, field_value)
                        to_update.append(intermediate)
        models.IntermediateModel.objects.bulk_update(to_update, ['irrelevant_field'])
        return super(ModelASerializer, self).update(instance, validated_data)


class MainModelSerializer(serializers.ModelSerializer):
    model_a = ModelASerializer(many=True)
    model_related_only_to_main_model = RelatedOnlyToMainModelSerializer(many=True)

    class Meta:
        model = models.MainModel
        fields = '__all__'

    def update(self, instance, validated_data):
        model_a_data = validated_data.pop('model_a', [])
        model_a_data_map = {str(a.get('id')): a for a in model_a_data}
        for a in instance.model_a.all():
            try:
                ModelASerializer(context=self.context).update(a, model_a_data_map[str(a.id)])
            except:
                pass
        validated_data.pop('model_related_only_to_main_model', None)
        return super(MainModelSerializer, self).update(instance, validated_data)

And the view:

from rest_framework.viewsets import ModelViewSet

from .models import MainModel
from .serializers import MainModelSerializer


class MainModelViewSet(ModelViewSet):
    queryset = MainModel.objects
    serializer_class = MainModelSerializer

    def get_queryset(self):
        return self.queryset.prefetch_related('model_related_only_to_main_model', 'model_a',
                                              'model_a__small_one_connectors',
                                              'model_a__small_one_connectors__smallest_model')

Command to populate the db

import random
from django.core.management.base import BaseCommand
from main.models import MainModel, RelatedOnlyToMainModel, SmallestModel, ModelA, IntermediateModel


class Command(BaseCommand):
    help = 'Populate the database with MainModel, RelatedOnlyToMainModel, ModelA, SmallestModel, and IntermediateModel instances'

    def handle(self, *args, **kwargs):
        main_model = MainModel.objects.create(one_of_many_fields=1)

        related_instances = [RelatedOnlyToMainModel.objects.create(field_1=i) for i in range(1, 6)]
        main_model.model_related_only_to_main_model.set(related_instances)

        model_a_instances = [ModelA.objects.create(main=main_model, some_field=(i % 2 == 0)) for i in range(10)]

        smallest_model_instances = [SmallestModel.objects.create(another_field=f'SmallestModel {i}') for i in range(300)]

        for smallest_model in smallest_model_instances:
            model_a_instance = random.choice(model_a_instances)
            IntermediateModel.objects.create(model_a=model_a_instance, smallest_model=smallest_model, irrelevant_field='Irrelevant')

        main_model.smallests.set(smallest_model_instances)

        self.stdout.write(self.style.SUCCESS('Database populated successfully'))

I’m looking for the way to decrease number of database queries (given models size it became much of a performance issues) required when sending the POST request (931 with the test data vs 6 at GET). I realize the ORM needs to do some queries to map and initialize the objects properly, but i’m unable to prefetch the data by hand for this operation. I would suspect similar way of doing this as for the GET method, where programmer is responsible for prefetching the data, and one can do it properly as one has knowledge about the models. I’ve tried many things such as overriding get_object() method of the serializer etc. but nothing seems to work. Given the complexity of my production models and serializers’ update() methods and poor test coverage, unfortunately i’m unable to create separate “SaveSerializers” that will not contain nested serializers and instead the update() would do anything “manually”. Does anyone knows the way to do it properly without losing existing functionalities e.g. model level defined validations?