Search code examples
pythondjangodjango-rest-frameworkdjango-orm

How to cache SerializerMethodField result


I have the below serializer:

class OrderItemResponseSerializer(serializers.ModelSerializer):
    prepack_qty = serializers.SerializerMethodField()
    product_description = serializers.SerializerMethodField()

    class Meta:
        model = OrderItem
        fields = (
            "product",
            "qty_ordered",
            "qty_approved",
            "status",
            "prepack_qty",
            "product_description"
        )

    def get_prepack_qty(self, obj):
        return obj.product.prepack_quantity

    def get_product_description(self, obj):
        return obj.product.product_description

When I make a get request to /orders, I make a lot of SQL queries to the database because different orders may contain the same product. How can I cache the result of get_prepack_qty and get_product_description methods? I tried to use @cached_property this way:

class OrderItem(models.Model):
    ...
    @cached_property
    def item_product_description(self):
        return self.product.product_description

But the number of requests to the database remained the same.


Solution

  • Well, first of all, I should say that what you have implemented in this piece of code below:

        ...
        @cached_property
        def item_product_description(self):
            return self.product.product_description
    

    And using @cached_property alone doesn't cache the data for you, you just created a property in the model for get_product_description serializer method, And this does not reduce the volume and number of your queries to the database at all; Of course you need .bind() method in your serializer method like below:

    class BlobSerializer(SerializerCacheMixin, serializers.Serializer):
       blobs = serializers.SerializerMethodField()
    
       def get_blobs(self, instance):
           # recursive serializer
           serializer = self.__class__(instance.results, many=True)
           serializer.bind('*', self)
           return serializer.data
    

    But in order to cache the result of this method As you asked in your question there is a good project in Pypi called drf-serializer-cache you can use it easily for this purpose, for example, the following piece of code is taken from the document of this project:

    from drf_serializer_cache import SerializerCacheMixin
    from rest_framework import serializer
    
    class ResultSerializer(SerializerCacheMixin, serializers.Serializer):
       results = serializers.SerializerMethodField()
    
       def get_results(self, instance):
           # recursive serializer
           serializer = self.__class__(instance.results, many=True)
           serializer.bind('*', self)  # bind call is essential for >efficient cache!
           return serializer.data
    

    Also if you want to implement it yourself in your project Seeing the implementation of SerializerCacheMixin object in this project can help you a lot or even use it directly.