Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is django-rest-frameworks request.data sometimes immutable?

In my restful CreateAPIView I mutate my request.data dictionary.

Occasionally I receive an error not caught by my tests:

This QueryDict instance is immutable

For e.g. this:

class CreateView(CreateAPIView):
    serializer_class = ...
    queryset = ...

    def post(self, request, *args, **kwargs):
        request.data['user'] = request.user.pk
        return self.create(request, *args, **kwargs)

request.data seems to be a normal dict in my tests. Why is it sometimes a QueryDict? How should this be dealt with? Should request.data not be mutated in general? How should you use the ModelSerializer class, when you need to populate some fields yourself?

like image 771
David Schumann Avatar asked Sep 17 '18 12:09

David Schumann


2 Answers

Why this occasional behavior?

When we look into the SC of Request (as @Kenny Ackerman mentioned), it return a QueryDict object if you are passing a form media type ('application/x-www-form-urlencoded' or 'multipart/form-data') data to the view class.
This check being execute within the is_form_media_type() method of Request class.

If you are passing a application/json data to the view, the request.data will be a dict object.

How to reproduce the behaviour?

It can be reproduce by using sending different ContentType data into view. (In POSTMAN tool, use form-data and raw JSON to get the behaviour)


How to get current logged-in user in serializer?

Method-1 pass extra argument to .save() (as @Linovia mentioned) by overriding the perform_create() method

class CreateView(CreateAPIView):
    serializer_class = ...
    queryset = ...

    def post(self, request, *args, **kwargs):
        request.data['user'] = request.user.pk
        return self.create(request, *args, **kwargs)

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

Method-2 Use CurrentUserDefault() class as below

from django.contrib.auth import get_user_model

User = get_user_model()


class MySerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())

    class Meta:
    # your code
like image 123
JPG Avatar answered Nov 23 '22 04:11

JPG


When you have to modify a QueryDict object received from a request, it is a immutable object, instead use this line of code if you wanna add attributes:

myNewRequest = request.GET.copy()
myNewRequest.data['some_attr'] = float(something)
like image 39
Unicone Avatar answered Nov 23 '22 04:11

Unicone