Marcos Aguayo
Marcos Aguayo

Reputation: 7170

Different field type serialiser - Django Rest Framework

Hello I have a serialiser and I'd like to have one field type for the GET requests and anther field for the POST requests.

These are my serialisers:

class TypeSerializer(serializers.Serializer):
    id = serializers.CharField()
    name = serializers.CharField(max_length=50)
    colour = serializers.CharField(max_length=8)

class UserSerializer(serializers.Serializer):
    id = UUIDField(format="hex_verbose")
    name = serializers.CharField()
    type = TypeSerializer()

So the response is something like this:

{
    "id": "987328lf-93ad-21ba-2648-h2u7b95d5cf",
    "name": "name",
    "type": {
        "id": "RANDOM_ID",
        "name": "Type 1",
        "colour": "#ffffff"
    }
}

That's what I want on the GET, but on the POST I'd like to send a payload like this:

{
    "name": "New name",
    "type": "RANDOM_ID"
}

I will get a 500 error because type is expected to be a dictionary.

Anyone knows if its posible to have one field for GET and another field for POST without creating another serialiser?

Upvotes: 2

Views: 2491

Answers (2)

Ken4scholars
Ken4scholars

Reputation: 6296

One way of going about this is using 2 fields - a read field and a write field:

class TypeSerializer(serializers.Serializer):
    id = serializers.CharField()
    name = serializers.CharField(max_length=50)
    colour = serializers.CharField(max_length=8)

class UserSerializer(serializers.Serializer):
    id = UUIDField(format="hex_verbose")
    name = serializers.CharField()
    type_read = TypeSerializer(read_only=True)

    # you could use a prrimary key related field 
    # instead if it is a model serializer;
    type = serializers.CharField(write_only=True) 

    def to_representation(self, instance):
        rep = super().to_representation(instance)

        # rename type_read to type
        rep['type'] = rep['type_read']
        del rep['type_read']
        return rep

Upvotes: 2

mon io
mon io

Reputation: 752

You need to override to_internal_value method:

class UserSerializer(serializers.Serializer):
    id = UUIDField(format="hex_verbose")
    name = serializers.CharField()
    type = TypeSerializer()

    def to_internal_value(self, data):
        type = data.get('type')
        if type:
            data['type'] = {'id': type}

        ret = super(UserSerializer, self).to_internal_value(data)

        # Probably here you will need to get type instance.

        return ret

I think that you want to use this serializer for creating user so it needs so more iprovements:

# Inherit from ModelSerializer !
class UserSerializer(serializers.ModelSerializer):
    id = UUIDField(format="hex_verbose")
    name = serializers.CharField()
    type = TypeSerializer()

    class Meta:
        model = User # Set model
        fields = ('id', 'name', 'type') # Set fields

    def to_internal_value(self, data):
        type = data.get('type')
        if type:
            data['type'] = {'id': type}

        ret = super(UserSerializer, self).to_internal_value(data)

        # Here i change dict to instance
        type = ret.get('type')
        if type:
            try:
                ret['type'] = Type.objects.get(id=type['id'])
            except Type.DoesNotExist:
                raise ValidationError({'type': ['Type with the given ID does not exist.']}
        return ret

Upvotes: 2

Related Questions