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

在Django REST Framework中实现动态嵌套序列化及答题完整性校验

解决方案:校验测验提交的完整性

首先得修正你当前序列化器的设计问题,否则会导致创建QuizCompletion时出现逻辑错误:

  • 现在的QuizSerializer是嵌套序列化,提交数据时如果传完整的Quiz信息,DRF会尝试新建一个Quiz,而不是关联已有的测验,这不符合需求
  • QuizAnswerSerializer里用QuizQuestionSerializer同理,应该用问题ID来关联已有问题,而非传递问题文本

第一步:修正序列化器

from rest_framework import serializers
from .models import Quiz, QuizQuestion, QuizCompletion, QuizAnswer

class QuizAnswerSerializer(serializers.ModelSerializer):
    # 接收quizquestion的ID,输出时可按需嵌套序列化
    quizquestion = serializers.PrimaryKeyRelatedField(queryset=QuizQuestion.objects.all())

    class Meta:
        model = QuizAnswer
        fields = ["quizquestion", "answer"]

class QuizCompletionSerializer(serializers.ModelSerializer):
    answers = QuizAnswerSerializer(many=True)
    # 关联已有Quiz,仅需传入ID
    quiz = serializers.PrimaryKeyRelatedField(queryset=Quiz.objects.all())

    class Meta:
        model = QuizCompletion
        fields = ["completed_by", "quiz", "answers"]

第二步:自定义validate方法实现完整性校验

你的方案a(在QuizCompletionSerializer中自定义validate()方法)是正确选择——这个校验需要同时用到quiz和answers两个字段的数据,属于跨字段校验,方案b的自定义字段校验器无法获取其他字段的值,不适用此场景。

在序列化器中添加validate方法:

def validate(self, data):
    quiz = data.get('quiz')
    submitted_answers = data.get('answers')

    # 获取当前测验的所有问题ID集合
    quiz_question_ids = set(quiz.questions.values_list('id', flat=True))
    # 获取提交答案对应的问题ID集合
    submitted_question_ids = set(answer['quizquestion'].id for answer in submitted_answers)

    # 校验1:提交的问题是否都属于当前测验
    invalid_question_ids = submitted_question_ids - quiz_question_ids
    if invalid_question_ids:
        raise serializers.ValidationError(f"问题ID {invalid_question_ids} 不属于当前测验")

    # 校验2:是否提交了所有问题的答案
    missing_question_ids = quiz_question_ids - submitted_question_ids
    if missing_question_ids:
        raise serializers.ValidationError(f"缺少问题ID {missing_question_ids} 的答案")

    # 校验3:是否重复提交同一个问题的答案
    if len(submitted_question_ids) != len(submitted_answers):
        raise serializers.ValidationError("存在重复提交的问题答案")

    return data

第三步:重写create方法处理嵌套创建

因为QuizAnswer依赖QuizCompletion,需要先创建QuizCompletion实例,再批量创建关联的答案,所以要重写序列化器的create方法:

def create(self, validated_data):
    answers_data = validated_data.pop('answers')
    # 创建QuizCompletion主实例
    quiz_completion = QuizCompletion.objects.create(**validated_data)
    # 批量创建关联的QuizAnswer
    for answer_data in answers_data:
        QuizAnswer.objects.create(quizcompletion=quiz_completion, **answer_data)
    return quiz_completion

方案选择说明

DRF的自定义字段校验器仅能针对单个字段内容校验,无法访问序列化器中其他字段的数据(比如这里需要拿到quiz的问题列表做对比)。而validate()方法可以获取所有已校验的字段数据,正好适配这种跨字段关联的校验场景,所以方案a是最优解。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:57:28