简体   繁体   中英

Optimizing DRF post request handling by preventing full response serialization back

I have defined AViewSet and ASerializer for my AModel :

class AModel(Model):
    name = CharField(16)
    text = TextField()
    related = ForeignField('ARelated')

class AViewSet(ModelViewSet):
    queryset = AModel.objects.all()
    serializer_class = ASerializer

class ASerializer(Serializer):
    class Meta(object):
        model = AModel
        fields = '__all__'

I wrote a RESTful client that posts a lot of data to that view/endpoint in multiple requests, creating many AModel records. I have noticed, however that a significant part of the server time is spent on generating the response , and upon googling for a bit I found several references to the nested relationship hazard which seems like a decent fix, but got me wondering:

I already know what I posted and I don't need the pk s, so could I prevent that serialization response from happening entirely? Can I instead just serialize the number of rows inserted?

Taking a look at DRF's CreateModelMixin class:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

I realized I could override the create method and reimplement it without returning the serializer.data as part of the response, so it'll look similar to this:

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response({}, status=status.HTTP_201_CREATED, headers=headers)

I have two questions regarding this approach:

  1. Does this practice of preventing the full serialization of objects created with a POST makes sense wrt RESTful design patterns, approach, ideology, etc?
  2. Will this actually avoid select ing all the related data (as well as execute any SerializerMethodField s, etc?

Let's take the basic ModelViewset and ModelSerializer combination here :) It will be like,

# serializers.py
class SampleSerializer(serializers.ModelSerializer):
    class Meta:
        model = SampleModel
        fields = '__all__'

# views.py
class SampleViewset(viewsets.ModelViewSet):
    queryset = SampleModel.objects.all()
    serializer_class = SampleSerializer

Why DRF returning all the data back to client ?
Here, the SampleViewset is using SampleSerializer everytime and it will serialilze all fields defined in the serializer class. As per the current configuration

What is the possible solution?
The possible solution is for this is stop serialization process of certain fields by some means :)

How to do?
As far as I knew, this can be done in two ways.
1. Use a minimal SampleSerializer class for POST method
2. override the to_representation() method of SampleSerializer on POST requests

Method-1 : Use different serializer
Define a new serializer class with fields which are you wish to send and retrive while POST request

class SampleSerializerMinimal(serializers.ModelSerializer):
    class Meta:
        model = SampleModel
        fields = ('id', 'name', 'age')

Now, we've to tell viewsets to use this serializer for POST methods, it can be done in get_serializer_class() of viewset

class SampleViewset(viewsets.ModelViewSet):
    queryset = SampleModel.objects.all()
    serializer_class = SampleSerializer

    



Method-2 : Override the to_representation() method

class SampleSerializer(serializers.ModelSerializer):
    class Meta:
        model = SampleModel
        fields = '__all__'

    



What is the best way ?
I felt Method-1 is more DRF way of doing things, but you can't add id only to the fields because, POST request may require more fields.
The Method-2 to is also good, but its not much clean if you want to return n fields, and writing it in your to_representation() method


UPDATE-1
Method-3: Combination of method-1 and method-2

# serializer.py
class (serializers.ModelSerializer):
    class Meta:
        model = SampleModel
        fields = ('id', 'name', 'age')

    


# views.py
class SampleViewset(viewsets.ModelViewSet):
    queryset = SampleModel.objects.all()
    serializer_class = SampleSerializer

    



UPDATE-2

Does this practice of preventing the full serialization of objects created with a POST makes sense wrt RESTful design patterns, approach, ideology, etc?

the .data is calling to_representation() method, which calls the related objects and all other fields in the serializer . ( Source code of data property of serializer ) So, if you can avoid that .data call, it would be nice!.
Since I've seen many API responses with single detail like {"status":true} after a POST request, I don't think your approch overkill the DRF patterns and other stuff

Will this actually avoid selecting all the related data (as well as execute any SerializerMethodFields, etc?

Yes. As I said above, It won't call the serializations process unless calling the .data

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM