Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Unable to modify the serializer.data dictionary in Django Rest Framework

I have tried to add a key serializer.data['test'] = 'asdf', this does not appear to do anything.

I want to transform the representation of a key's value. To do this, I'm trying to use the value to calculate a new value and replace the old one in the dictionary.

This is what I want to accomplish, but I don't know why the value is not being replaced. There are no errors thrown, and the resulting dictionary has no evidence that I've tried to replace anything:

class PlaceDetail(APIView):
    def get(self, request, pk, format=None):
        place = Place.objects.select_related().get(pk=pk)
        serializer = PlaceSerializer(place)
        #serializer.data['tags'] = pivot_tags(serializer.data['tags'])
        serializer.data['test'] = 'asdf'
        print(serializer.data['test'])
        return Response(serializer.data)

Terminal: KeyError: 'test'

I have observed by printing that serializer.data is a dictionary.

I have also tested that the syntax I'm trying to use should work:

>>> test = {'a': 'Alpha'}
>>> test
{'a': 'Alpha'}
>>> test['a']
'Alpha'
>>> test['a'] = 'asdf'
>>> test
{'a': 'asdf'}

How can I properly modify the serializer.data dictionary?

like image 595
davidtgq Avatar asked Feb 16 '16 02:02

davidtgq


1 Answers

The Serializer.data property returns an OrderedDict which is constructed using serializer._data. The return value is not serializer._data itself.

Thus changing the return value of serializer.data does not change serializer._data member. As a consequence, the following calls to serializer.data are not changed.

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

# In class ReturnDict(OrderedDict)
def __init__(self, *args, **kwargs):
    self.serializer = kwargs.pop('serializer')
    super(ReturnDict, self).__init__(*args, **kwargs)

You can keep a copy of the return value of serializer.data, which is an ordered dictionary, and manipulate it as you wish.

Example:

# keep the return value of serializer.data
serialized_data = serializer.data
# Manipulate it as you wish
serialized_data['test'] = 'I am cute'
# Return the manipulated dict
return Response(serialized_data)

Why:

If you look at the source code of Django Restframework, you will see that in Serializer class,

  • Serializer._data is just a normal dictionary.
  • Serializer.data is a method decorated to act like a property. It returns a ReturnDict object, which is a customized class derived from OrderedDict. The returned ReturnDict object is initialized using key/value pairs in Serializer._data.

If Serializer.data returns Serializer._data directly, then your original method will work as you expected. But it won't work since it's returning another dictionary-like object constructed using Serializer._data.

Just keep in mind that the return value of Serializer.data is not Serializer._data, but an ordered dictionary-like object. Manipulating the return value does not change Serializer._data.

I believe the reason why serializer.data does not return serializer._data directly is to avoid accidental change of the data and to return a pretty representation of serializer._data.

like image 85
Rikka Avatar answered Sep 20 '22 17:09

Rikka