How to implement `like feature` in Django REST properly

Hi, this is my first time implementing a like system in my project. Below is my implementation, which I developed using various sources: tips from chat rooms and AI. But since I have no experience with this, I would like to learn best practices for implementing a like chip that is used in work projects. After all, my implementation seems a bit crude to me.

My model ‘Like’ and relation with model ‘Recipe’

class Like(models.Model):
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["user", "recipe"], name="unique_user_recipe"
            )
        ]
    liked_users = models.ManyToManyField(get_user_model(), through="Like", related_name="liked_recipes", blank=True)

view

class LikeToggleAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def post(self, request, slug):
        user = request.user
        recipe = get_object_or_404(Recipe, slug=slug)
        like_obj = Like.objects.filter(user=user, recipe=recipe).first()
        
        if like_obj:
            like_obj.delete()
            is_liked = False
        else:
            Like.objects.create(user=user, recipe=recipe)
            is_liked = True
            
        return Response({"is_liked": is_liked, "like_count": recipe.liked_users.count()}, status=status.HTTP_200_OK)

I would be happy to hear your advice!

You could simplify this a bit by removing the Like model definition - you don’t need it. The ManyToManyField - which I’m assuming is in your Recipe model, is sufficient for what you need. (Note that by default, a many-to-many field already enforces the unique constraint.)

Based upon your code:

You will then use recipe.liked_users.add(user) and recipe.liked_users.remove(user) to add and remove the liked status. (No need to do any direct work on the through model. This means you can remove the like_obj query and replace the Like create call.)

Checking for the existance of the relationship would be something like:
recipe.liked_users.filter(id=user.id)

2 Likes

Thanks, this really simplified my code. Still wondering in what cases a separate model and Like or Bookmark serializer is used for such functionality. The only additional fields that come to mind are created_at.

In this situation. the model still exists - it’s just that it’s created and managed internally, it’s not something you need to define.

Yes, having a requirement for additional fields would make it necessary for you to define the model to support that data. In addition to a created_at, you could have an additional field to support “reaction_type”, such as Facebook’s “Like”, “Love”, “Care” or LinkedIn’s “Like”, “Celebrate”, “Support”, etc choices.

Adding additional values like these will not fundamentally alter your code. The add method provides a through_defaults parameter for specifying those values when objects are being related.

1 Like