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字段的校验等待视图层赋值,因此直接返回必填项错误。
修复方案
调整序列化器的字段配置,区分读/写场景的字段逻辑,将关联对象的匹配逻辑前移到序列化器校验层,视图层仅处理登录用户自动绑定的逻辑即可:
- 首先修改
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
- 简化视图层的
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
相关产品推荐
相关产品推荐

