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?
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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With