# DRF Serializer POST method data prefetch

**URL:** https://forum.djangoproject.com/t/drf-serializer-post-method-data-prefetch/33098
**Category:** Using the ORM
**Created:** [July 19, 2024, 6:09am UTC](https://forum.djangoproject.com/t/drf-serializer-post-method-data-prefetch/33098 "2024-07-19T06:09:36Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![Eragorn](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/eragorn/32/22714_2.png) [@Eragorn](https://forum.djangoproject.com/u/Eragorn)
#### Post date: [July 19, 2024, 6:09am UTC](https://forum.djangoproject.com/t/drf-serializer-post-method-data-prefetch/33098/1 "2024-07-19T06:09:36Z")

</div>

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

```auto
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):

```auto
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:

```auto
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

```auto
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?
