如何在Django DRF中实现点赞切换(已点赞则取消)功能?
Django 实现点赞/取消点赞切换功能
你的核心需求是实现点赞状态切换逻辑:用户对指定帖子操作时,若已点赞则取消(删除Like记录),未点赞则添加点赞。你之前尝试在Serializer中编写post方法的思路有误——Serializer仅负责数据的序列化与反序列化,请求逻辑需在View层实现。以下是完整解决方案:
1. 优化点赞模型(可选但推荐)
给Like模型添加唯一约束,从根源避免同一用户重复点赞同一帖子:
# models.py class Like(models.Model): created_by = models.ForeignKey(User, on_delete=models.CASCADE) post = models.ForeignKey(Post, on_delete=models.CASCADE) class Meta: unique_together = ('created_by', 'post') # 新增唯一约束,防止重复点赞 def __str__(self): return f"{self.created_by.username} 点赞了帖子 {self.post.id}"
2. 实现View层逻辑
方案一:重写ModelViewSet的create方法
如果想保留原有的LikesView结构,直接重写create方法实现切换逻辑:
# views.py from rest_framework import viewsets, status from rest_framework.response import Response from .models import Like, Post from .serializers import LikeSerializer class LikesView(viewsets.ModelViewSet): queryset = Like.objects.all() serializer_class = LikeSerializer def perform_create(self, serializer): serializer.save(created_by=self.request.user) def create(self, request, *args, **kwargs): post_id = request.data.get('post_id') try: post = Post.objects.get(pk=post_id) except Post.DoesNotExist: return Response({"error": "帖子不存在"}, status=status.HTTP_404_NOT_FOUND) # 检查当前用户是否已点赞该帖子 like_exists = Like.objects.filter(post=post, created_by=request.user).exists() if like_exists: # 已点赞,执行取消操作 Like.objects.filter(post=post, created_by=request.user).delete() return Response({"message": "取消点赞成功"}, status=status.HTTP_200_OK) else: # 未点赞,执行创建操作 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)
方案二:使用APIView单独实现(更清晰)
若不需要ModelViewSet的完整CRUD功能,单独写一个APIView处理点赞切换更简洁:
# views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import Like, Post class ToggleLikeView(APIView): def post(self, request, post_id): try: post = Post.objects.get(pk=post_id) except Post.DoesNotExist: return Response({"error": "帖子不存在"}, status=status.HTTP_404_NOT_FOUND) # get_or_create会返回(实例, 是否新建)的元组 like, created = Like.objects.get_or_create(post=post, created_by=request.user) if not created: # 实例已存在,说明是取消点赞 like.delete() return Response({"message": "取消点赞成功"}, status=status.HTTP_200_OK) return Response({"message": "点赞成功"}, status=status.HTTP_201_CREATED)
同时在urls.py中添加对应路由:
# urls.py router = routers.DefaultRouter() router.register('likes', LikesView) router.register('post', PostView) router.register('comment', CommentView) urlpatterns = [ path('toggle-like/<int:post_id>/', ToggleLikeView.as_view()), # 新增点赞切换路由 ] + router.urls
3. 调整Serializer
Serializer中无需手动定义post_id,通过配置extra_kwargs设置字段读写权限即可:
# serializers.py class LikeSerializer(serializers.ModelSerializer): created_by = serializers.StringRelatedField(read_only=True) post = serializers.StringRelatedField(read_only=True) class Meta: model = Like fields = ('id', 'created_by', 'post', 'post_id') extra_kwargs = { 'post_id': {'write_only': True} # post_id仅用于写入,不返回给前端 }
为什么你之前的代码无效?
Serializer的设计目的是处理数据格式转换,不负责HTTP请求的逻辑处理,因此在Serializer中定义post方法无法被框架识别执行,请求逻辑必须放在View层实现。
内容的提问来源于stack exchange,提问作者ahmet murat
相关产品推荐
相关产品推荐

