[Django]-How can modify request.data in django REST framework

19👍

In case your API is APIView then you should use update function to expand your request data object without losing the data sent from the client-side.

request.data.update({"id": "10", "user": "tom"})

16👍

request.data should be an immutable QueryDict, rather than a string. If you need to modify it:

if isinstance(request.data, QueryDict): # optional
    request.data._mutable = True
request.data['age'] = "30"

The only reason you might check if it’s an instance of QueryDict is so it’s easier to unit test with a regular dict.

8👍

A good friend just took me to school on a much simpler approach than I illustrate above

class CreateSomething(CreateAPIView):
    model = Something
    queryset = Something.objects.all()
    serializer_class = SomethingSerializer

    perform_create(self,serializer):
    def perform_create(self,serializer):
        ip = self.get_ip()
        ## magic here: add kwargs for extra fields to write to db
        serializer.save(ip_addr=ip)

    def get_ip(self):
        x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR',None)
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = self.request.META.get('REMOTE_ADDR',None)
        return ip

class SomethingSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(validators=[UniqueValidator(queryset=Something.objects.all())])
    fieldA = serializers.CharField()
    fieldB = serializers.CharField()

    class Meta:
        model = Customer2
        fields = ['email','fieldA','fieldB','ip_addr']
        read_only_fields = ['ip_addr']

8👍

Generally request in drf views is rest_framework.request.Request instance. Following to it’s source code (djangorestframework==3.8.2):

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data

You can do:

request._full_data = your_data

3👍

If your endpoint is implemented with a DRF (Django REST Framework) ViewSet the solution can be to implement the corresponding serializer class’s to_internal_value method and modify the data there.

class MyModelViewSet(viewsets.ModelViewSet):
    authentication_classes = ...
    ...
    serializer_class = MyModelSerializer


class MyModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyModel
        fields = ('id', 'user', ...)

    def to_internal_value(self, data):
        instance = super(MyModelSerializer, self).to_internal_value(data)
        if "lastModified" in data:
            # instance["id"] = 10  # That's sketchy though
            instance["user"] = "tom"
        return instance

2👍

It looks like a json string. To convert it to a dict you should do:

import json
data = json.loads(request.data)

then you can add extra attributes:

data['age'] = 30

Then you will have to make a new request because it seem like you cant just change the old one. This assumes that you are posting to /notes/:

from rest_framework.test import APIRequestFactory
factory = APIRequestFactory()
request = factory.post('/notes/', data, format='json')

2👍

If you are afraid of altering your request object, then use deep copy to copy the object and then you can easily alter it.

Usage::

from copy import deepcopy

# here is your other code and stuffs
data = deepcopy(request.data)

feel free to alter the data as you want as it is now mutable.

So far, this is my preferred way of altering if not using a generic view.

For any drawbacks of this method, please comment below!

1👍

Though other answers are good but i just wanted to add one thing here;

We have now two ways to update a request.data object But before that, check if it is a QueryDict (as already mentioned by @mikebridge);

from django.http.request import QueryDict


if isInstance(request.data, QueryDict):
    request.data._mutable = True

After that, to update the request.data, first way is;

request.data.update({'key': 'new_value'})

this will work fine, but say if the request.data[‘key’] which you want to update is a list, then the value will not get changed completely by the new_value but it will get appended to the older list which may cause trouble and you may not get the desired result.

So to overcome that, i.e, to completely change the value of some key, use the second method;

request.data['key'] = 'new_value'

This will change the value of request.data['key'] completely to new_value.

0👍

According to your comment:

“because before posting i need to chnage the field names aqs required by the API”

You should be using the Field‘s source argument instead.

This will make error messages more consistent otherwise your user will face errors with field names they didn’t provide.

0👍

I have dealt with this differently. I override the CreateAPIView create method, as follows

class IPAnnotatedObject(CreateAPIView):
    model = IPAnnotatedObject
    queryset = IPAnnotatedObject.objects.all()
    serializer_class = IPAnnotatedObject
    def create(self, request, *args, **kwargs):
        request.data['ip_addr'] = self.get_ip()
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        ## perform_create calls serializer.save() which calls the serializer's create() method
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def get_ip(self):
        x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR',None)
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = self.request.META.get('REMOTE_ADDR',None)
        return ip

The corresponding serializer class looks like follows

class IPAnnotatedObjectSerializer(serializers.ModelSerializer):
    email = serializers.EmailField(validators=[UniqueValidator(queryset=IPAnnotatedObject.objects.all())])
    password = serializers.CharField(write_only=True)
    ip_addr = serializers.IPAddressField(write_only=True)
    class Meta:
        model = IPAnnotatedObject
        fields = ['email','password','created_ip']

    def create(self, validated_data):
        email, password, created_ip = validated_data['email'], validated_data['password'],validated_data['created_ip']
        try:
            ipAnnoObject = IPAnnotatedObject.objects.create(email=email,password=make_password(password),ip_addr=ip_addr)
        except Exception as e:
            # you can think of better error handler
            pass
        return ipAnnoOjbect

0👍

I did the following:

import json

data = json.dumps(request.data)
data = json.loads(data)

data['age'] = 100

now use variable data instead of request.data.

0👍

May be override is_valid in your serializer class will be more readable

class YourSerializer
    
    # Don't forget it
    age = serializers.IntegerField()

    def is_valid(self, raise_exception: bool = ...) -> bool:
        self.initial_data["age"] = 30
        return super().is_valid(raise_exception)

Leave a comment