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
.
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