Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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')
like image 274
meowmeow Avatar asked Sep 08 '17 08:09

meowmeow


People also ask

Do we need Serializers in Django REST framework?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

What is HyperlinkedModelSerializer in Django?

HyperlinkedModelSerializer is a layer of abstraction over the default serializer that allows to quickly create a serializer for a model in Django. Django REST Framework is a wrapper over default Django Framework, basically used to create APIs of various kinds.

What is UUID Django?

UUID, Universal Unique Identifier, is a python library that helps in generating random objects of 128 bits as ids. It provides the uniqueness as it generates ids on the basis of time, Computer hardware (MAC etc.). Universally unique identifiers are a good alternative to AutoField for primary_key .

What does rest stand for Django?

Django Rest Framework makes it easy to use your Django Server as an REST API. REST stands for "representational state transfer" and API stands for application programming interface. You can build a restful api using regular Django, but it will be very tidious. DRF makes everything easy.


1 Answers

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.

  • http://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield
  • http://www.django-rest-framework.org/api-guide/fields/#uuidfield
like image 77
illagrenan Avatar answered Sep 19 '22 13:09

illagrenan