Search code examples
pythondjangodjango-rest-frameworkdjango-querysetdjango-rest-viewsets

How to serialize data more efficiently in Django Rest Framework?


The part of my problem is solved by the question: #72534250
I understood that the path I was following was wrong, and I should change the optimization of the models to serializers and/or viewsets.
But how?

I had this structure:
*I already removed element_name and element_text from Models Foo

    class User(models.Model):
        name = models.CharField(max_lenth=50)
        age = models.IntergerField()
    class Customer(models.Model):
        user = models.ForeingKey(User, on_delete=models.CASCADE, verbose_name='User')
    class Element(models.Model):
        name = models.CharField(max_lenth=50)
        text = models.TextField()
        
    class Bar(models.Model):
        element = models.ForeingKey(Element, on_delete=models.CASCADE, verbose_name='Element')
    class Foo(models.Model):
        bar = models.OneToOneField(Bar, on_delete=models.CASCADE, verbose_name='Bar')
        customer = models.ForeingField(Customer, on_delete=models.CASCADE, verbose_name='Customer')
        is_active = models.BooleanField('Is Active', default=False)
        
        def user(self):
            return self.customer.user
        
        # Removed
        def element_name(self):
            return self.bar.element.name

        # Removed
        def element_text(self):
            return self.bar.element.text

And these serializers:

    class UserSerializer(ModelSerializer):
        class Meta:
            model = User
            fields = '__all__'
    class CustomerSerializer(ModelSerializer):
        class Meta:
            model = Customer
            fields = '__all__'
    class ElementSerializer(ModelSerializer):
        class Meta:
            model = Element
            fields = '__all__'
    class BarSerializer(ModelSerializer):
        element = ElementSerializer()

        class Meta:
            model = Bar
            fields = '__all__'
    class FooSerializer(ModelSerializer):
        bar = BarSerializer()
        user = UserSerializer()
        
        class Meta:
            model = Foo
            fields = '__all__'

And this viewset:

    class FooViewSet(ModelViewSet):
        serializer_class = FooSerializer
        permission_classes = [IsAuthenticated]
        authentication_classes = [JWTAuthentication]
        http_method_names = ['get', 'post', 'patch']

        def get_queryset(self):
           active = self.request.query_params.get('is_active', False)
           name = self.request.query_params.get('name', '')
           data = {'is_active': active}
           if name == 'Fire':
              data['bar__element__name'] = name
           queryset = Foo.objects.filter(**data)
           return queryset
        

I try this tutorial, but I didn't notice any real performance improvement. So I'm looking for another way to solve this problem.

I thought... If I just put select_related(...) in the return of get_queryset, how Django Rest would identify that it is to serialize the cached data? Would that really improve performance?

I also need element_name, element_text and object user to be returned in the GET of FooViewSet. How can I return them in the most efficient way?


Solution

  • how Django Rest would identify that it is to serialize the cached data? Would that really improve performance?

    All the data that has been fetched by select_related are in your model object instances. So since the serializers as well as the model properties you defined essentially use these instances, they will have access to this "cached" data directly.

    I also need element_name, element_text and object user to be returned in the GET of FooViewSet. How can I return them in the most efficient way?

    In line with this, you can get them all in one query (including what element_name and element_text needs) with:

    queryset = Foo.objects.select_related(
        "bar__element",
        "customer__user",
    ).filter(**data)