9
votes

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
Why would you want to remove it ? It'll be correctly rendered by the json renderer.Linovia
You're absolutely correct! Thank you!meowmeow
You question makes sense @meowmeow. Because what we expect from a serializer output are native types. UUID is not a native type. This also causes me a problem when I write test checking that what I post on an endpoint produce a correct json, when I use DRF: self.assertDictEqual(posted_data, produced_data). Because it will compare a UUID in a string, with a UUID() object.matt

4 Answers

15
votes

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.

1
votes

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
votes

you can try to use, serializers.CharField

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

UUID will be corrected when rendered by JSONRenderer.