Pass request context to serializer from Viewset in Django Rest Framework

I have a case where the values for a serializer field depend on the identity of the currently logged in user. I have seen how to add the user to the context when initializing a serializer, but I am not sure how to do this when using a ViewSet, as you only supply the serializer class and not the actual serializer instance.

Basically I would like to know how to go from:

class myModelViewSet(ModelViewSet):
   queryset = myModel.objects.all()
   permission_classes = [DjangoModelPermissions]
   serializer_class = myModelSerializer

to:

class myModelSerializer(serializers.ModelSerializer):
    uploaded_by = serializers.SerializerMethodField()
    special_field = serializers.SerializerMethodField()

    class Meta:
        model = myModel

    def get_special_field(self, obj):
        if self.context['request'].user.has_perm('something.add_something'):
           return something

Sorry if it wasn't clear, from the DOCs: Adding Extra Context Which says to do

serializer = AccountSerializer(account, context={'request': request})
serializer.data

But I am not sure how to do that automatically from the viewset, as I only can change the serializer class, and not the serializer instance itself.


GenericViewSet has the get_serializer_context method which will let you update context:

class myModelViewSet(ModelViewSet):
    queryset = myModel.objects.all()
    permission_classes = [DjangoModelPermissions]
    serializer_class = myModelSerializer

    def get_serializer_context(self):
        context = super(myModelViewSet, self).get_serializer_context()
        context.update({"request": self.request})
        return context

For Function based views you can pass request or user as follow:

serializer = ProductSerializer(context = {'request':request},data=request.data)

Your Serializer may look like:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model    = Product
        fields   = ['id']

    def create(self, validated_data):
        user =  self.context['request'].user
        print("User is")
        print(user)

Feel free to inform if there is any better way to do this.


just use get_serializer() in your viewsets

def get_serializer(self, *args, **kwargs):
    """
    Return the serializer instance that should be used for validating and
    deserializing input, and for serializing output.
    """
    serializer_class = self.get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_class(*args, **kwargs)

Return parent context in overrided function get_serializer_context will make it easy to access request and its data.

 class myModelViewSet(ModelViewSet):
       queryset = myModel.objects.all()
       permission_classes = [DjangoModelPermissions]
       serializer_class = myModelSerializer

       def get_serializer_context(self):
       """
       pass request attribute to serializer
       """
           context = super(myModelViewSet, self).get_serializer_context()
           return context

This is very stable as every time we request viewset, it returns context as well.


the values for a serializer field depend on the identity of the currently logged in user

This is how I handle such cases in my ModelViewSet:

def perform_create(self, serializer):

    user = self.request.user
    if user.username == 'myuser':
        serializer.data['myfield'] = 'something'

    serializer.save()