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

如何用Django View Set实现课程收藏的重复添加校验?

解决课程收藏重复添加的问题

一、修正模型(添加唯一性约束)

你的MyCoursesList模型缺少用户与课程的唯一性校验,导致同一用户可以重复收藏同一课程。需要添加约束确保组合唯一:

class MyCoursesList(models.Model):
    user = models.ForeignKey('accounts.User', on_delete=models.CASCADE, blank=True)
    courses = models.ForeignKey(Courses, on_delete=models.CASCADE, blank=True, related_name='my_courses')
    added_date = models.DateTimeField(auto_now_add=True)
    teacher = models.ForeignKey(Teacher, on_delete=models.DO_NOTHING, default=1)

    class Meta:
        # 强制同一用户不能重复收藏同一课程
        unique_together = ('user', 'courses')
        # Django 2.2+ 推荐用以下写法替代unique_together
        # unique_constraints = [
        #     models.UniqueConstraint(fields=['user', 'courses'], name='unique_user_course')
        # ]

二、重构视图逻辑

原视图用ModelViewSet会附带很多不必要的接口(比如列表、更新),且错误地在get_queryset中处理创建逻辑。我们改用ViewSet重写create方法,实现校验与收藏逻辑:

from rest_framework import viewsets, status
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import MyCoursesList, Courses
from .serializers import MyCoursesListSerializer

class AddtoMyCoursesView(viewsets.ViewSet):
    serializer_class = MyCoursesListSerializer

    def create(self, request):
        # 获取当前登录用户(需确保接口已做登录校验)
        current_user = request.user
        course_id = request.data.get('pk')
        
        # 参数校验:课程ID不能为空
        if not course_id:
            return Response({'error': '课程ID不能为空'}, status=status.HTTP_400_BAD_REQUEST)
        
        # 校验课程是否存在
        course = get_object_or_404(Courses, id=course_id)
        
        # 检查当前用户是否已收藏该课程
        if MyCoursesList.objects.filter(user=current_user, courses=course).exists():
            return Response({'message': '您已收藏过该课程'}, status=status.HTTP_400_BAD_REQUEST)
        
        # 未收藏则创建收藏记录
        serializer = self.serializer_class(data={
            'courses': course.id,
            # 若课程关联了教师,自动从课程中获取教师ID,替代默认值1
            'teacher': course.teacher.id if hasattr(course, 'teacher') else 1
        })
        
        if serializer.is_valid():
            # 自动传入当前用户,无需前端传参
            serializer.save(user=current_user)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

三、序列化器优化(可选)

如果希望用户字段无需前端传入,可在序列化器中设置为只读:

from rest_framework import serializers
from .models import MyCoursesList

class MyCoursesListSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyCoursesList
        fields = ['id', 'user', 'courses', 'added_date', 'teacher']
        # 设置user为只读,由视图自动传入
        read_only_fields = ['user']

原代码问题说明

  1. ModelViewSet不适合单一的收藏创建场景,会生成多余接口;
  2. get_queryset方法被错误用于处理创建逻辑,该方法的作用是返回视图操作的查询集,而非处理请求;
  3. exists()返回布尔值,无法解包为两个变量,且未关联当前用户,导致校验逻辑错误;
  4. 缺少用户与课程的唯一性约束,即使视图校验通过,数据库层面仍可能出现重复数据。

内容的提问来源于stack exchange,提问作者Ashti Nematian

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:36:30