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

如何在Django REST mixins中使用嵌套序列化器实现多模型CRUD

问题说明
  • 目标是在Django REST Framework中实现多模型关联CRUD,单次API请求可同步完成Student、Course、Homework三张关联表的增删改查操作
  • 当前故障:接口无法正确返回Course、Homework关联模型的字段数据,API交互界面也无法展示关联模型字段
  • 问题根源集中在三类:模型关联定义冗余冲突、序列化器查询逻辑和语法错误、视图方法传参错误
现有代码错误点
  1. 模型层问题
    • Course、Homework模型同时定义了指向Student的外键和多对多字段,关联关系重复冗余,反向查询逻辑混乱
    • 模型配置的多对多反向访问名related_name和序列化器里写的obj.courses_set.all()、obj.homework_set.all()不匹配,根本查不到关联数据
  2. 序列化器问题
    • HomeworkSerializer中fields = __all__漏写引号,会直接触发语法错误
    • 用SerializerMethodField定义的关联字段默认只读,完全不支持写入、更新操作,没法实现关联表的新增/修改
    • All_Serializer中定义了students字段但Meta类的fields列表未包含该字段,属于无效代码
  3. 视图层问题
    • put方法调用update时手动传student_id=None,会覆盖路由传入的真实学生ID,还传入了未定义的homeworks、courses变量,请求直接报错
    • 查询集未预加载关联数据,会产生N+1查询问题,接口性能差
修正后实现代码

修正后的models.py

删除冗余的外键字段,统一关联关系配置:

import uuid
from django.db import models

class Student(models.Model):
    student_id = models.UUIDField(default=uuid.uuid4, unique=True,
                      primary_key=True, editable=False)
    firstName = models.CharField(max_length=20)
    age = models.IntegerField(default=18)


class Course(models.Model):
    courseName = models.CharField(max_length=20)
    courseYear = models.IntegerField(default=2021)
    students = models.ManyToManyField(Student, related_name='courses')


class Homework(models.Model):
    hwName = models.CharField(max_length=20)
    hwPossScore = models.IntegerField(default=100)
    course = models.ForeignKey(Course, related_name='homeworks', on_delete=models.CASCADE, null=True, blank=True)
    students = models.ManyToManyField(Student, related_name='homeworks')

修正后的serializers.py

用可写嵌套序列化器替代只读的SerializerMethodField,实现关联数据的同步写入:

from rest_framework import serializers
from .models import Student, Course, Homework

class HomeworkSerializer(serializers.ModelSerializer):
    class Meta:
        model = Homework
        fields = "__all__"

class CourseSerializer(serializers.ModelSerializer):
    homeworks = HomeworkSerializer(many=True, required=False)
    class Meta:
        model = Course
        fields = "__all__"

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"

class All_Serializer(serializers.ModelSerializer):
    courses = CourseSerializer(many=True, required=False)
    homeworks = HomeworkSerializer(many=True, required=False)

    def create(self, validated_data):
        courses_data = validated_data.pop('courses', [])
        homeworks_data = validated_data.pop('homeworks', [])
        student = Student.objects.create(**validated_data)

        # 创建关联课程及课程下作业
        for course_data in courses_data:
            course_homeworks = course_data.pop('homeworks', [])
            course = Course.objects.create(**course_data)
            course.students.add(student)
            for hw_data in course_homeworks:
                homework = Homework.objects.create(course=course, **hw_data)
                homework.students.add(student)

        # 创建直接关联学生的作业
        for hw_data in homeworks_data:
            homework = Homework.objects.create(**hw_data)
            homework.students.add(student)
        return student

    def update(self, instance, validated_data):
        # 更新学生基础字段
        instance.firstName = validated_data.get('firstName', instance.firstName)
        instance.age = validated_data.get('age', instance.age)
        instance.save()

        # 更新关联课程(示例为全量替换逻辑,可按需改为增量更新)
        if 'courses' in validated_data:
            instance.courses.clear()
            for course_data in validated_data.pop('courses'):
                course_homeworks = course_data.pop('homeworks', [])
                course = Course.objects.create(**course_data)
                course.students.add(instance)
                for hw_data in course_homeworks:
                    Homework.objects.create(course=course, **hw_data)
        
        # 更新关联作业(示例为全量替换逻辑,可按需改为增量更新)
        if 'homeworks' in validated_data:
            instance.homeworks.clear()
            for hw_data in validated_data.pop('homeworks'):
                homework = Homework.objects.create(**hw_data)
                homework.students.add(instance)
        return instance

    class Meta:
        model = Student
        fields = ('student_id','firstName','age','homeworks','courses')

修正后的views.py

修复传参错误,添加关联数据预加载:

from rest_framework import generics, mixins
from .models import Student
from .serializers import All_Serializer

class Main_mixins(generics.GenericAPIView, 
                                mixins.ListModelMixin, 
                                mixins.CreateModelMixin,
                                mixins.RetrieveModelMixin,
                                mixins.UpdateModelMixin,
                                mixins.DestroyModelMixin):
    serializer_class = All_Serializer
    queryset = Student.objects.prefetch_related('courses__homeworks', 'homeworks').all()
    lookup_field = 'student_id'

    def get(self, request, student_id=None):
        if student_id:
           return self.retrieve(request, student_id)
        return self.list(request)
    
    def post(self, request):
        return self.create(request)
    
    def put(self, request, student_id=None):
        return self.update(request, student_id)
    
    def delete(self, request, student_id=None):
        return self.destroy(request, student_id)

注意:上述代码中关联数据的更新为全量替换逻辑,如果业务需要支持关联数据的增量修改、编辑已有课程/作业而非每次新建,可在create/update方法中增加主键判断,查询到已有记录后执行更新操作即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 23:45:53