1

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?

1 Answer 1

3

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)
Sign up to request clarification or add additional context in comments.

3 Comments

In that case what would be the correct way to proceed if I wanted my 'uid' in my response and not 'flow_uid' while keeping the structure of the python object intact?
Thank you for your help, however I feel like something's still wrong here, I still want basic field validation to happen. For example, I tried giving an Integer to my Charfield and it just converted it to str instead of telling me something's wrong. Probably related to: stackoverflow.com/questions/53963371/…
DRF char field code says: "We're lenient with allowing basic numerics to be coerced into strings, but other types should fail. Eg. unclear if booleans should represent as true or True, and composites such as lists are likely user error."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.