I am working on implemeting Like/Unlike feature in API for mobile app via Django Rest Framework.
So I have following model:
class PlaylistLikes(models.Model):
user = models.ForeignKey(User)
playlist = models.ForeignKey(Playlist)
like = models.BooleanField(default=0, blank=True)
class Meta:
unique_together = ('user', 'playlist',)
verbose_name = "Playlist like"
verbose_name_plural = "Playlist likes"
I added unique_together to restrict user to like a playlist which he has already liked.
My view:
class LikesView(viewsets.ModelViewSet):
queryset = PlaylistLikes.objects.all()
serializer_class = LikesSerializer
filter_backends = (filters.OrderingFilter, filters.DjangoFilterBackend, filters.SearchFilter)
filter_class = LikesFilter
search_fields = ('playlist', 'user',)
def create(self, request, *args, **kwargs):
user = request.user
request.data['user'] = user.pk
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.data, status=status.HTTP_201_CREATED)
@detail_route(methods=['put'])
def put(self, request, *args, **kwargs):
user = request.user
request.data['user'] = user.pk
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data, status=status.HTTP_200_OK)
And serializer:
class LikesSerializer(serializers.ModelSerializer):
def create(self, validated_data):
like = PlaylistLikes.objects.create(**validated_data)
playlist = Playlist.objects.get(pk=like.playlist.id)
playlist.likes_count += 1
playlist.save()
return validated_data
def update(self, instance, validated_data):
like = PlaylistLikes.objects.get(**validated_data)
like.like = False
like.save()
playlist = Playlist.objects.get(pk=like.playlist.id)
playlist.likes_count -= 1
playlist.save()
return validated_data
class Meta:
model = PlaylistLikes
fields = ('user', 'playlist', 'like')
Urls:
router.register(r'likes', views.LikesView, 'likes')
To like playlist, I send POST method with body playlist = 19, user = 3, like = 1. Like is succesfully created.
To unlike same playlist, I send PUT method with body: playlist = 19, user = 3, but I got following error:
"non_field_errors": [ "The fields user, playlist must make a unique set." ]
I understand that it's happening because of unique_together in model. But I am not creating a new record with same user and playlist. I just whant to update existing record. Why this happens and how to solve this problem?
This happens because in the PUT method handler, you create a serializer without specifying a instance:
serializer = self.get_serializer(data=request.data)
Look at Serializer.__init__():
def __init__(self, instance=None, data=empty, **kwargs):
self.instance = instance
if data is not empty:
self.initial_data = data
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
kwargs.pop('many', None)
super(BaseSerializer, self).__init__(**kwargs)
You didn't provide the instance argument, and it defaults to None.
For a update, unique_together validation should be skipped, only if instance attribute is not None:
class UniqueTogetherValidator(object):
def exclude_current_instance(self, attrs, queryset):
"""
If an instance is being updated, then do not include
that instance itself as a uniqueness conflict.
"""
if self.instance is not None:
return queryset.exclude(pk=self.instance.pk)
return queryset
To solve this problem, you can move the query of Userlikes object before initializing the serializer:
like = PlaylistLikes.objects.get(**query)
serializer = self.get_serializer(like, data=request.data)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With