如何重写DRF序列化器的update方法以保存嵌套序列化器
嘿,作为DRF新手确实容易在重写update方法时踩坑,我来帮你捋清楚怎么处理这个问题!
首先先把你提供的代码规范格式化一下(顺便补了on_delete,不然Django会报错):
你的模型代码
class Course_Module(models.Model): name = models.CharField(max_length=200) class Quiz(models.Model): quiz_name = models.CharField(max_length=200) module_referred = models.ForeignKey( Course_Module, related_name="quiz", on_delete=models.CASCADE # 必须添加on_delete参数,Django 2.0+强制要求 )
你的序列化器(假设完整的嵌套字段是Quiz_QuestionSerializer)
class QuizSerializer(serializers.HyperlinkedModelSerializer): questions = Quiz_QuestionSerializer(many=True) # 嵌套序列化器,关联Quiz的问题 class Meta: model = Quiz fields = ['url', 'quiz_name', 'module_referred', 'questions']
接下来,重写update方法的核心是处理普通字段、外键关联、嵌套字段这三类数据,DRF默认不会自动帮你处理嵌套字段的更新,必须手动写逻辑。下面给你两种常见场景的示例:
场景1:只更新基础字段(含外键)
如果不需要处理questions这类嵌套字段,只更新quiz_name和module_referred,可以这样写:
def update(self, instance, validated_data): # 1. 更新普通字符串字段:如果validated_data里有值就用新的,没有就保留原字段 instance.quiz_name = validated_data.get('quiz_name', instance.quiz_name) # 2. 处理外键字段:这里分两种常见情况 module_data = validated_data.pop('module_referred', None) if module_data: # 情况A:前端传入的是Course_Module的主键(pk) if isinstance(module_data, int): module = Course_Module.objects.get(pk=module_data) instance.module_referred = module # 情况B:前端传入的是Course_Module的完整数据(比如要同时更新module的name) else: module = instance.module_referred module.name = module_data.get('name', module.name) module.save() # 3. 保存实例并返回(必须返回更新后的instance,否则DRF会报错) instance.save() return instance
场景2:需要更新嵌套的questions字段
如果要同时更新关联的Quiz_Question(创建新问题、更新旧问题、删除未提及的问题),可以扩展逻辑:
def update(self, instance, validated_data): # 先处理基础字段和外键(同场景1的代码) instance.quiz_name = validated_data.get('quiz_name', instance.quiz_name) module_data = validated_data.pop('module_referred', None) if module_data: if isinstance(module_data, int): module = Course_Module.objects.get(pk=module_data) instance.module_referred = module else: module = instance.module_referred module.name = module_data.get('name', module.name) module.save() instance.save() # 处理嵌套的questions字段 questions_data = validated_data.pop('questions', []) existing_question_ids = [q.id for q in instance.questions.all()] for question_data in questions_data: question_id = question_data.get('id') # 如果问题ID存在,更新现有问题 if question_id and question_id in existing_question_ids: question = instance.questions.get(id=question_id) # 根据你的Quiz_Question模型字段更新,比如text、score等 question.text = question_data.get('text', question.text) question.save() existing_question_ids.remove(question_id) # 如果ID不存在,创建新问题 else: instance.questions.create(**question_data) # 删除那些没在更新列表里的旧问题(可选,根据业务需求决定是否保留) instance.questions.filter(id__in=existing_question_ids).delete() return instance
几个关键提醒
- 重写
update方法必须返回更新后的instance,否则DRF会抛出异常 - 如果用
HyperlinkedModelSerializer,外键字段默认是HyperlinkedRelatedField,前端需要传入完整的模块URL;如果想让前端传pk更方便,可以把外键字段改成PrimaryKeyRelatedField:module_referred = serializers.PrimaryKeyRelatedField(queryset=Course_Module.objects.all()) - 嵌套字段的更新逻辑完全取决于你的业务需求——比如是否允许删除旧问题、是否允许修改问题关联的quiz等,按需调整代码就行
内容的提问来源于stack exchange,提问作者Shashishekhar Hasabnis
相关产品推荐
相关产品推荐

