3

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.

Community
  • 1
  • 1
Jacob Windsor
  • 6,750
  • 6
  • 33
  • 49

1 Answers1

1

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

user = UserSerializer(required=False)
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Sorry, should have said. I already tried this and it has no effect. Also removing `read_only` is not an option since I don't want the User instance to be edited – Jacob Windsor Jul 20 '16 at 17:41
  • @JacobWindsor You still getting `user field is required` with this? Can you post the stacktrace please? – Rohit Jain Jul 20 '16 at 17:50
  • yes, i get the exact same error. Might be a stupid question but is there a simple way to get the stacktrace in DRF? – Jacob Windsor Jul 20 '16 at 17:56
  • I had a problem where a StringRelatedField was being required. Adding both read_only=True and required=False got me running again, though I don't quite see why both would be needed. – Rick Dec 24 '16 at 17:56