Pass request context to serializer from Viewset in Django Rest Framework

DjangoDjango Rest-FrameworkSerialization

Django Problem Overview


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.

Django Solutions


Solution 1 - Django

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

Solution 2 - Django

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.

Solution 3 - Django

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)

Solution 4 - Django

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.

Solution 5 - Django

> 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()

Solution 6 - Django

Simply add this 2 line method in your class and you are good to go.

def get_serializer_context(self):
    return {'request': self.request}

Solution 7 - Django

since the posted answers had partial correctness, summarizing here in the interest of completeness.

  1. override get_serializer_context..AND
  2. use get_serializer in your views instead of manually calling the serializer

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionoowowaeeView Question on Stackoverflow
Solution 1 - DjangoSysloView Answer on Stackoverflow
Solution 2 - DjangoSourabh SInhaView Answer on Stackoverflow
Solution 3 - Djangosteven.yanView Answer on Stackoverflow
Solution 4 - DjangoShashankView Answer on Stackoverflow
Solution 5 - DjangoostergaardView Answer on Stackoverflow
Solution 6 - DjangoAnkit KumarView Answer on Stackoverflow
Solution 7 - Djangouser2457199View Answer on Stackoverflow