Gaëtan GOUSSEAUD
Gaëtan GOUSSEAUD

Reputation: 92

Validated data fields differs from data fields Django Rest Nested serializers

So I was playing around with serializers in django and wanted to change the names of my fields in my response when I realized my changes had not been taken in count I did some digging and saw that my validated_data differs from my data.

My goal here is to give a python object to a serializer which has different fields than the name I want to return so I used the 'source=' argument to my field.

Note that changing the name of the python object's field is not an option.

Here's the python object:

class Flow(object):
    """Just a regular python object"""
    def __init__(self, name=None, flow_uid=None, data_type=None, parent=None, container=None):
        """This has more fields than the serializer is waiting"""
        self._parent = None
        self._container = None
        self.name = name
        self.data_type = data_type
        self.flow_uid = flow_uid

And the following serializers (I am using a nested representation)
serializers.py

from rest_framework.fields import CharField, IntegerField, ListField, JSONField
from rest_framework.serializers import Serializer

class OutputSerializer(Serializer):
    uid = CharField(max_length=36)
    name = CharField(max_length=100)
    description = CharField(max_length=100)


class FlowSerializer(Serializer):
    uid = CharField(source='flow_uid', max_length=36) # I want 'uid' in my response not 'flow_uid'
    name = CharField(max_length=100)
    data_type = CharField(max_length=100)
    class Meta:
        fields = '___all___'

    def to_representation(self, instance):
        instance = super(FlowSerializer, self).to_representation(instance)
        #Here instance = OrderedDict([('uid', 'uid_value'), ('name', 'name_value'), ('data_type', 'data_value')])
        return instance


class FlowOutputSerializer(OutputSerializer):
    columns = FlowSerializer(many=True)

viewsets.py

class AddTransformationViewSet(ViewSet):
    """Handle available "actions" for BrickModel operations"""

    def list(self, request, parent_lookup_analyses: str):
        """The method I call for this test"""

        flow1 = Flow(name="name1", flow_uid='flow_uid_value1', data_type='str')
        flow2 = Flow(name="name2", flow_uid='flow_uid_value2', data_type='str')
        flow1_ser = FlowSerializer(flow1)
        flow2_ser = FlowSerializer(flow2)

        dummy_col = {
            "name": "output_name",
            "description": "output_description",
            "uid": "output_uid",
            "columns":
            [
                flow2_ser.data, # Debug: {'uid': 'flow_uid_value2', 'name': 'name2', 'data_type': 'str'}
                flow1_ser.data # Debug: {'uid': 'flow_uid_value1', 'name': 'name1', 'data_type': 'str'}
            ]
        }
        #Debug dummy_col: {'name': 'output_name', 'description': 'output_description', 'uid': 'output_uid', 'columns': [{'uid': 'flow_uid_value2', 'name': 'name2', 'data_type': 'str'}, {'uid': 'flow_uid_value1', 'name': 'name1', 'data_type': 'str'}]} 


        dummy_serializer: FlowOutputSerializer = FlowOutputSerializer(data=dummy_col)
        dummy_serializer.is_valid(raise_exception=True)
        # Debug dummy_serializer.data: {'uid': 'output_uid', 'name': 'output_name', 'description': 'output_description', 'columns': [OrderedDict([('uid', 'flow_uid_value2'), ('name', 'name2'), ('data_type', 'str')]), OrderedDict([('uid', 'flow_uid_value1'), ('name', 'name1'), ('data_type', 'str')])]}
        # Debug dummy_serializer.validated_data: OrderedDict([('uid', 'output_uid'), ('name', 'output_name'), ('description', 'output_description'), ('columns', [OrderedDict([('flow_uid', 'flow_uid_value2'), ('name', 'name2'), ('data_type', 'str')]), OrderedDict([('flow_uid', 'flow_uid_value1'), ('name', 'name1'), ('data_type', 'str')])])])
        return Response(data=dummy_serializer.validated_data, status=status.HTTP_201_CREATED)

Expected_response:

{
    ...
    "columns": [
        {
            "uid": "flow_uid_value2",
            "name": "name2",
            "data_type": "str"
        },
        {
            "uid": "flow_uid_value1",
            "name": "name1",
            "data_type": "str"
        }
    ]
}

What I get (I want 'flow_uid' to be 'uid'):

{
    ...
    "columns": [
        {
            "flow_uid": "flow_uid_value2",
            "name": "name2",
            "data_type": "str"
        },
        {
            "flow_uid": "flow_uid_value1",
            "name": "name1",
            "data_type": "str"
        }
    ]
}

Is there any particular danger in using .data in this case rather than .validated_data? What is the cause of this behavior?

Upvotes: 1

Views: 437

Answers (1)

Linovia
Linovia

Reputation: 20976

Is there any particular danger in using .data in this case rather than .validated_data? What is the cause of this behavior?

serializer.validated_data is meant to be used with the Python object. Therefore it will expose flow_uid because of the custom source value.

serializer.data will be the serialised result of the save() after save has been called.

Therefore you should always be using serializer.data in your responses and keep serializer.validated_data in any code that interacts with models or internal project code:

Response(data=dummy_serializer.data, status=status.HTTP_201_CREATED)

Upvotes: 3

Related Questions