Jacob Windsor
Jacob Windsor

Reputation: 6980

Rest Framework Serializer Field Read-Only not working

I have a problem where a read-only attribute is not behaving as expected. I have looked at this question and other sources to try and solve the issue but to no avail.

The endpoint is for saving and listing user favorites. Users can favourite multiple object types so I use the content types framework.

# serializers.py
class FavouriteSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)
    type = serializers.CharField(source='content_type', read_only=True)
    object = FavouriteObjectRelatedField(read_only=True)
    content_type = serializers.CharField(write_only=True)
    object_id = serializers.IntegerField(write_only=True)

    def create(self, validated_data, *args, **kwargs):
        content_object = Favourites.objects.get_content_object(validated_data.get('object_id'),
                                                               validated_data.get('content_type'))
        user = kwargs.get('user')
        return Favourites.objects.create_favourite(content_object, user)

    class Meta:
        model = Favourites
        fields = ('type', 'object','user','content_type','object_id')

This is the FavouriteObjectRelatedField serializer that is required to dynamically serialize an object based on its type.

# serializers.py
class FavouriteObjectRelatedField(serializers.RelatedField):
    """
    A custom serializer to use for favourited objects.
    """
    def to_representation(self, value):
        """
        Serialize favourited objects using their respective serializer
        """
        if isinstance(value, Track):
            track = TrackBuilder(value).get_info()
            serializer = TrackSerializer(track, context=self.context, read_only=True)

        elif isinstance(value, News):
            serializer = NewsSerializer(value, context=self.context, read_only=True)

        else:
            raise Exception("Unexpected type of favourited object")

        return serializer.data

And the view. I override the perform_create method so to add the user instance which is retrieved from a url parameter.

#views.py
class UserFavouritesList(generics.ListCreateAPIView):
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly)
    serializer_class = FavouriteSerializer

    def get_user(self):
        user_id = self.kwargs.get('user_id')
        try:
            return get_user_model().objects.get(pk=user_id)
        except get_user_model().DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

    def perform_create(self, serializer):
        serializer.save(user=self.get_user())

    def get_queryset(self):
        return Favourites.objects.user_favourites(self.get_user())

I always get a user field required error. However, if I removed the user field this error is obviously gone. Interestingly, if I rename the user field to owner I still get the same error with the field name specified as user. Error here:

{
    "user": [
        "This field is required."
    ]
}

Update 1

As with one of the answers, I have already tried setting required=True but it has no effect.

Removing read_only=True is not an option since the user instance shouldn't be editable from this endpoint.

Update 2

Another interesting development: if I remove the type field it works as expected. Strange but somehow accessing the content_type field must have an effect on validation.

Upvotes: 3

Views: 4234

Answers (1)

Rohit Jain
Rohit Jain

Reputation: 213223

Try setting required=False in there, instead of read_only=True.

user = UserSerializer(required=False)

Upvotes: 1

Related Questions