Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override data property of serializer in DRF

I am using django-rest-framework-mongoengine for a personal project. I want to be able to send extra data in list requests. I have written 2 mixins for that purpose:

UserSearializerContextMixin: Collects the list of user ids for all the instances present in the list.

class UserSerializerContextMixin(object):
    user_lookup_field = 'user_id'
    user_fields_required = ['id','full_name','image','level']
    _user_ids = []

    def update_context(self,user_id):
        if not self.context.get('user_ids'):
            self.context['user_ids'] = [user_id]
        else:
            self.context['user_ids'].append(user_id)

    def to_representation(self,instance):
        self.update_context(getattr(instance,self.user_lookup_field))
        return super(UserSerializerContextMixin,self).to_representation(instance)

UserSerializerDataMixin: Override the data property using the context prepared in to_representation part.

class UserSerializerDataMixin(object):

    @property   
    def data(self):

        ret = super(UserSerializerDataMixin, self).data
        // Override the data
        return ReturnDict(ret, serializer=self)

Then for my serializer I do something like this:

class DFSerializer(UserSerializerContextMixin,UserSerializerDataMixin,DocumentSerializer):
    //Fields and all

But somehow the code just does not enter the overridden data property. I guess logically the data property should be overridden by extending the mixin. But it does not happen here.

What could possibly be the reason and how to resolve?

like image 544
Animesh Sharma Avatar asked Nov 25 '15 07:11

Animesh Sharma


1 Answers

This is a super old question, but just in case somebody else stumbles upon this:

I came here because I was unhappy how drf requires a different serializer output for TemplateHTMLRenderer than it does for the other renderers. So one of the possible solutions involved overriding data property to return a dict with serializer & data instead of ReturnList containing the same serializer & data.

Anyway, the problem here is that for listing records in the ViewSet, it is not your serializer instanced directly, but a ListSerializer instead and that ListSerializer then invokes your serializer for each particular record to be serialized.

An ugly patch "fixing" this problem could be made like so:

class YourViewSet(SomeBaseViewSet):

....

def get_serializer(self, *args, **kwargs):

    res = super().get_serializer(*args, **kwargs)

    class Patch(res.__class__):

        @property
        def data(self):
            request = self.context['request']
            if isinstance(request.accepted_renderer, TemplateHTMLRenderer):
                return dict(data=super().data,
                            serializer=self.child if isinstance(self, serializers.ListSerializer) else self)
            return super().data

    res.__class__ = Patch

    return res

As of time of this writing, I'm still deciding what the best approach for solving my particular problem (TemplateHTMLRenderer requiring different serialized output) is. I'm testing overriding the renderer next, but the above does "solve" my issue for me and it also explains why code didn't behave to OP's expectations.

like image 123
velis Avatar answered Sep 18 '22 12:09

velis