You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 21:10:36