user2989731
user2989731

Reputation: 1359

Override serializer.data in Django REST Framework

I've been trying to alter the value of a form field the Django REST Framework's admin panel and for some reason the change never takes place. I have the serializer below

class SomeView(ModelViewSet):
  queryset = MyModel.objects.all()
  serializer_class = MyModelSerializer

  # I Want to override this and change the POST data
  def perform_create(self, serializer):
     user = self.request.user.id

     # this was a form field where I manually entered the user ID
     # I now want it to default to the logged in user
     serializer.data['user'] = user

     # This returns the original user that was entered into the form field
     print serializer.data

I checked out serializer.data with dir() and it's just a python dictionary so I can't figure out why I can't modify the value. As a test, I tried to add extra values but that doesn't work either

# this doesnt work
serializer.data['some_new_field'] = 'test'

EDIT

On another note, I can copy the data and edit it

fake_data = serializer.data.copy()
fake_data['old_value'] = 'new value'

However, it always fails to validate

serializer = MyModelSerializer(data=fake_data)
serializer.is_valid() # returns false

EDIT EDIT:

Ok, so the validation error was caused by Django returning a SimpleLazyObject. Everything works now when I perform a copy of the data, but I'm really curious as to why I can't edit serializer.data directly without copying it. The problem is solved now, but if anyone can provide insight on the issue just for curiosity, that would be awesome.

Upvotes: 15

Views: 8525

Answers (1)

dhke
dhke

Reputation: 15388

I checked out serializer.data with dir() and it's just a python dictionary so I can't figure out why I can't modify the value.

While the value returned from Serializer.data is indeed a dictionary, Serializer.data is not a simple instance variable.

If you look at rest_framework/serializers.py:

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    # [...]
    @property
    def data(self):
        ret = super().data
        return ReturnDict(ret, serializer=self)

ReturnDict inherits from OrderedDict, but you still get a new dictionary every time you access Serializer.data.

The real data is in _data, however as noted by the underscore you might not want to modify that either as it is not intended to be public. The values are filled by Serializer.to_representation() which you could override on the viewset.

As for the second part: ModelViewSet defines get_serializer() that is called with the request POST data to create the serializer you want to modify. I'd suggest try to change the input data before the serializer is created, instead.

Upvotes: 15

Related Questions