Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST framework flat, read-write serializer

In Django REST framework, what is involved in creating a flat, read-write serializer representation? The docs refer to a 'flat representation' (end of the section http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects) but don't offer examples or anything beyond a suggestion to use a RelatedField subclass.

For instance, how to provide a flat representation of the User and UserProfile relationship, below?

# Model
class UserProfile(models.Model):
    user = models.OneToOneField(User)
    favourite_number = models.IntegerField()

# Serializer
class UserProfileSerializer(serializers.ModelSerializer):
    email = serialisers.EmailField(source='user.email')
    class Meta:
        model = UserProfile
        fields = ['id', 'favourite_number', 'email',]

The above UserProfileSerializer doesn't allow writing to the email field, but I hope it expresses the intention sufficiently well. So, how should a 'flat' read-write serializer be constructed to allow a writable email attribute on the UserProfileSerializer? Is it at all possible to do this when subclassing ModelSerializer?

Thanks.

like image 475
Paul Pepper Avatar asked Aug 19 '13 16:08

Paul Pepper


People also ask

What is serializer 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 difference between serializer and ModelSerializer?

The ModelSerializer class is the same as a regular Serializer class, except that: It will automatically generate a set of fields for you, based on the model. It will automatically generate validators for the serializer, such as unique_together validators. It includes simple default implementations of .

How do you pass extra context data to Serializers in Django REST Framework?

In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.

What is To_representation in Django serializer?

to_representation(self, value) method. This method takes the target of the field as the value argument, and should return the representation that should be used to serialize the target. The value argument will typically be a model instance.


1 Answers

Looking at the Django REST framework (DRF) source I settled on the view that a DRF serializer is strongly tied to an accompanying Model for unserializing purposes. Field's source param make this less so for serializing purposes.

With that in mind, and viewing serializers as encapsulating validation and save behaviour (in addition to their (un)serializing behaviour) I used two serializers: one for each of the User and UserProfile models:

class UserSerializer(serializer.ModelSerializer):
    class Meta:
        model = User
        fields = ['email',]

class UserProfileSerializer(serializer.ModelSerializer):
    email = serializers.EmailField(source='user.email')
    class Meta:
        model = UserProfile
        fields = ['id', 'favourite_number', 'email',]

The source param on the EmailField handles the serialization case adequately (e.g. when servicing GET requests). For unserializing (e.g. when serivicing PUT requests) it is necessary to do a little work in the view, combining the validation and save behaviour of the two serializers:

class UserProfileRetrieveUpdate(generics.GenericAPIView):
    def get(self, request, *args, **kwargs):
        # Only UserProfileSerializer is required to serialize data since
        # email is populated by the 'source' param on EmailField.
        serializer = UserProfileSerializer(
                instance=request.user.get_profile())
        return Response(serializer.data)

    def put(self, request, *args, **kwargs):
        # Both UserProfileSerializer and UserProfileSerializer are required
        # in order to validate and save data on their associated models.
        user_profile_serializer = UserProfileSerializer(
                instance=request.user.get_profile(),
                data=request.DATA)
        user_serializer = UserSerializer(
                instance=request.user,
                data=request.DATA)
        if user_profile_serializer.is_valid() and user_serializer.is_valid():
            user_profile_serializer.save()
            user_serializer.save()
            return Response(
                    user_profile_serializer.data, status=status.HTTP_200_OK)
        # Combine errors from both serializers.
        errors = dict()
        errors.update(user_profile_serializer.errors)
        errors.update(user_serializer.errors)
        return Response(errors, status=status.HTTP_400_BAD_REQUEST)
like image 118
Paul Pepper Avatar answered Sep 19 '22 03:09

Paul Pepper