Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework: how serializer context works?

It's a pretty standard task in Django REST Framework to supply additional args/kwargs to a serializer to set values of fields set not via request.data, but via the value in url parameters or cookies. For instance, I need to set user field of my Comment model equal to request.user upon POST request. Those additional arguments are called context.

Several questions (1, 2) on StackOverflow suggest that I override get_serializer_context() method of my ModelViewSet. I did and it doesn't help. I tried to understand, what's wrong, and found out that I don't understand from the source code, how this context system is supposed to work in general. (documentation on this matter is missing, too)

Can anyone explain, where serializer adds context to normal request data? I found two places, where it saves the values from context.

  1. serializer.save(), method, which mixes kwargs with validated data, but it is usually called with no arguments (e.g. by ModelMixins).
  2. fields.__new__(), which caches args and kwargs, but it seems that nobody ever reads them later.
like image 827
Boris Burkov Avatar asked May 17 '16 11:05

Boris Burkov


People also ask

What is context in Django serializer?

Context are used in Django REST Framework when you want to add extra data to the serializer in addition to the object being serialized.

How does Django REST framework serializer work?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

How does Django validate data in serializer?

Validation in Django REST framework serializers is handled a little differently to how validation works in Django's ModelForm class. With ModelForm the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class.

How do I pass Queryset to serializer?

To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.


2 Answers

Whenever you use generic views or viewsets, DRF(3.3.2) adds request object, view object and format to the serializer context. You can use serializer.context to access, lets say request.user in the serializer.

This is added when get_serializer_class() is called. Inside that, it calls get_serializer_context() method where all these parameters are added to its context.

DRF source code for reference:

class GenericAPIView(views.APIView):
    """
    Base class for all other generic views.
    """

    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)    

    def get_serializer_context(self):
        """
        Extra context provided to the serializer class.
        """
        return {
            'request': self.request,
            'format': self.format_kwarg,
            'view': self
        }
like image 172
Rahul Gupta Avatar answered Sep 20 '22 11:09

Rahul Gupta


to set values of fields set not via request.data, but via the value in url parameters or cookies. For instance, I need to set user field of my Comment model equal to request.user upon POST request.

This is how I handle both cases in my ModelViewSet:

def perform_create(self, serializer):

    # Get article id from url e.g. http://myhost/article/1/comments/
    # obviously assumes urls.py is setup right etc etc
    article_pk = self.kwargs['article_pk']
    article = get_object_or_404(Article.objects.all(), pk=article_pk)

    # Get user from request
    serializer.save(author=self.request.user, article=article)

Unfortunately the nested objects is not standard for DRF but that's besides the point. :)

like image 32
ostergaard Avatar answered Sep 22 '22 11:09

ostergaard