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

Django DRF重写perform_create创建Course报400必填字段错误

问题根因

400错误是序列化器校验阶段拦截导致的,你写的perform_create逻辑根本没有执行,核心问题有两个:

  • DRF的请求校验发生在perform_create调用之前,只有序列化器is_valid()校验通过才会进入perform_create的保存逻辑
  • 你在CourseSerializer中将category、sub_category、teacher三个外键字段声明为嵌套序列化器,且没有设置read_only=True,DRF默认会将这类嵌套序列化器标记为必填可写字段,要求请求体必须传入符合嵌套序列化器结构的完整对象,既不接受你传入的分类名称字符串,也不会自动跳过teacher字段的校验等待视图层赋值,因此直接返回必填项错误。
修复方案

调整序列化器的字段配置,区分读/写场景的字段逻辑,将关联对象的匹配逻辑前移到序列化器校验层,视图层仅处理登录用户自动绑定的逻辑即可:

  1. 首先修改CourseSerializer,将返回详情用的嵌套序列化器设为只读,新增只写字段接收前端传入的分类名称,在校验阶段完成分类实例的匹配:
from user.nested_serializers.teacher_for_course import CourseTeacherSerializer
from rest_framework import serializers
from .models import Course
from category.nested_serializers.for_course import CourseCategorySerializer
from sub_category.nested_serializers.for_course import CourseSubCategorySerializer
from category.models import Category
from sub_category.models import SubCategory


class CourseSerializer(serializers.ModelSerializer):
    # 读场景返回关联对象详情,设为只读不参与写入校验
    category = CourseCategorySerializer(read_only=True)
    sub_category = CourseSubCategorySerializer(read_only=True)
    teacher = CourseTeacherSerializer(read_only=True)
    marked_as_favorite_by = CourseTeacherSerializer(many=True, read_only=True)
    
    # 只写字段,接收前端传入的分类名称
    category_name = serializers.CharField(write_only=True, required=True)
    sub_category_name = serializers.CharField(write_only=True, required=True)

    class Meta:
        model = Course
        fields = ('id', 'name', 'description', 'created_at',
                  'updated_at', 'premium_only', 'duration', 'level', 'category', 'sub_category', 'teacher', 'marked_as_favorite_by', 'category_name', 'sub_category_name')
    
    def validate(self, attrs):
        # 校验阶段匹配分类实例,不存在直接返回400错误
        category_name = attrs.pop('category_name')
        sub_category_name = attrs.pop('sub_category_name')
        try:
            attrs['category'] = Category.objects.get(name=category_name)
        except Category.DoesNotExist:
            raise serializers.ValidationError({"category_name": "指定分类不存在"})
        try:
            attrs['sub_category'] = SubCategory.objects.get(name=sub_category_name)
        except SubCategory.DoesNotExist:
            raise serializers.ValidationError({"sub_category_name": "指定子分类不存在"})
        
        # 可选:校验子分类是否归属对应父分类,避免数据错乱
        if attrs['sub_category'].category_id != attrs['category'].id:
            raise serializers.ValidationError({"sub_category_name": "子分类与所选父分类不匹配"})
        return attrs
  1. 简化视图层的perform_create逻辑,仅绑定当前登录用户即可:
class CreateCourseView(CreateAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    permission_classes = [IsAuthenticated]

    def perform_create(self, serializer):
        serializer.save(teacher=self.request.user)

修改后接口请求体只需要传基础课程字段+category_name、sub_category_name即可,teacher字段会自动绑定当前登录用户,返回结果中会正常展示嵌套的分类、教师详情。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:54:33