Skip to content

Memory usage optimization on Serializer class #7250

@Anton-Shutik

Description

@Anton-Shutik

Steps to reproduce

Lets say we have model like this:

class User(models.Model):
    first_name = models.CharField()
    last_name = models.CharField()

and serializer like this:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('first_name', 'last_name')

and whenever we serialize python obj, the data property has reference to serializer:

serializer = UserSerializer(instance=User.objects.first())
serializer.data.serializer is serializer # gives true

It works fine on small objects like in the example, but it starts consuming more memory with larger querysets and nested Serializers, just because of that data.serializer reference. Python reference counter cannot delete the data.serializer object, because data property references on that, and such a way we have to wait until GC collects it.

The data.serializer reference is only used (at least it is single usage I found) in HTMLFormRenderer and it looks like overhead. Also I'm pretty sure there are lots of projects the do not use the renderer at all, but have to "suffer" from that extra memory usage.

If I change our serializer like below, it consumes less memory:

class UserSerializer(serializers.ModelSerializer):

    @property
    def data(self):
        ret = super(Serializer, self).data
        #return ReturnDict(ret, serializer=self)    <----original code
        return ReturnDict(ret)

So, my point is to make the serializer reference optional, depending if HTMLFormRenderer is used or not, or pass it via renderer context.

class GenericAPIView(views.APIView):

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['needs_serializer_ref'] = HTMLFormRenderer in self.renderer_classes
        return context

and then in serializer do:

class Serializer(BaseSerializer):

    @property
    def data(self):
        ret = super(Serializer, self).data
        if self.context.get('needs_serializer_ref'):
            return ReturnDict(ret, serializer=self)
        return ret

Just an idea.

Thoughts ?

PS.
Ready to submit a PR

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions