Django Rest API: How to get rid of 'UUID' in json when serializing models?

8.9k Views Asked by At

Why does 'UUID' appear in front of the value of 'profile' key and how do I remove it properly?

roster/serializers.py

class ShiftSerializer(serializers.ModelSerializer):

class Meta:
    model = Shift
    fields = ('id', 'profile', 'location', 'date', 'start_time', 'end_time')

profile/models.py

class Profile(models.Models):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)

roster/models.py

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
    profile = models.ForeignKey('profiles.Profile', null=True, blank=True)

python manage.py shell

from roster.models import Shift
from roster.serializers import ShiftSerializer

myshift = Shift.objects.first()
serializer = ShiftSerializer(myshift)
serializer.data

Output:

{'id': '92ca258e-8624-434a-b61d-e1cd3b80e0e8', 'profile': UUID('0081b028-0a11-47fb-971e-c47177ed93be')
4

There are 4 best solutions below

0
On BEST ANSWER

You can rewrite representation ,like this

class ShiftSerializer(serializers.ModelSerializer):
    class Meta:
        model = Shift
        fields = '__all__'

    def to_representation(self, obj):
        return {
            "id": obj.id,
            "profile": obj.profile.id,
            "location": obj.location,
            "date": obj.date,
            "start_time": obj.start_time,
        }
0
On

you can try to use, serializers.CharField

class ShiftSerializer(serializers.ModelSerializer):
     profile = serializers.CharField(read_only=True)
0
On

UUID will be corrected when rendered by JSONRenderer.

1
On

tl;dr

See The solution at the bottom.

The problem

Attribute .data on Serializer should return only primitive representation of object (http://www.django-rest-framework.org/api-guide/serializers/#baseserializer). This should be done by calling to_representation() method (http://www.django-rest-framework.org/api-guide/serializers/#to_representationself-obj) on a serializer and all fields.

@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):

    # ...
    # ...

    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        # ...
        # ...

        for field in fields:

            # ...
            # ...

            if check_for_none is None:
                ret[field.field_name] = None
            else:
                ret[field.field_name] = field.to_representation(attribute)

        return ret

Source: https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py#L505-L529

There is a problem with models that have uuid.UUID as a primary key. PrimaryKeyRelatedField returns uuid.UUID instance - this is clearly not a primitive type which leads to e.g. UUID('') is not JSON serializable errors.

When pk_field attribute on PrimaryKeyRelatedField is not set, to_representation method simply returns uuid.UUID instance, see the related code:

class PrimaryKeyRelatedField(RelatedField):
    # ...

    def to_representation(self, value):
        if self.pk_field is not None:
            return self.pk_field.to_representation(value.pk)
        return value.pk

Source: https://github.com/encode/django-rest-framework/blob/master/rest_framework/relations.py#L269-L272

Why is it a problem

As stated in other answers and comments, JSONRenderer will correctly handle this issue (http://www.django-rest-framework.org/api-guide/serializers/#serializing-objects)

from rest_framework.renderers import JSONRenderer

json_data = JSONRenderer().render(serializer.data)

But there are situations when you do not want to use JSONRenderer:

  • You're comparing .data during unit testing;
  • You need to store .data in database, file, ...
  • You want to post .data via requests to some API: requests.post(..., json=serializer.data)
  • ...

The solution

Set pk_field attribute on PrimaryKeyRelatedField to UUIDField():

from rest_framework import serializers
from rest_framework.fields import UUIDField


class ExampleSerializer(serializers.ModelSerializer):
    id = serializers.PrimaryKeyRelatedField(required=True,
                                            allow_null=False,
                                            # This will properly serialize uuid.UUID to str:
                                            pk_field=UUIDField(format='hex_verbose'))

And uuid.UUID instances will be correctly serialized to str when accessing serializer.data.