Django ORM多对多关系实战指南 - 教程

一、Django 多对多关系的原理

在关系型数据库中,多对多关系通常需要 第三张中间表 来维护两张表之间的对应关系。
在 Django 中,你只需要定义 ManyToManyField,Django 会自动帮你创建这张中间表。

特点:

  • 可以双向查询(正向 + 反向)
  • 支持添加、删除、清空操作(add()remove()clear()
  • 可以通过 through 参数自定义中间表,添加额外字段(例如时间戳、角色等)

二、实战案例:学生和课程系统

假设我们有一个 学生(Student)课程(Course) 的关系:

  • 一个学生可以选多门课程
  • 一门课程可以被多个学生选

1. 定义模型

# models.py
from django.db import models
class Course
(models.Model):
name = models.CharField(max_length=100) # 课程名称
teacher = models.CharField(max_length=100) # 授课老师
def __str__(self):
return self.name
class Student
(models.Model):
name = models.CharField(max_length=100) # 学生姓名
age = models.IntegerField()
# 多对多关系
courses = models.ManyToManyField(Course, related_name="students")
def __str__(self):
return self.name

执行 python manage.py makemigrations && python manage.py migrate 后,Django 会自动生成一张中间表:

appname_student_courses  # student_id, course_id

在这里插入图片描述


2. 数据操作示例

创建数据
# 创建课程
math = Course.objects.create(name="数学", teacher="张老师")
english = Course.objects.create(name="英语", teacher="李老师")
# 创建学生
s1 = Student.objects.create(name="小明", age=18)
s2 = Student.objects.create(name="小红", age=19)
# 关联课程
s1.courses.add(math, english) # 小明选了数学、英语
s2.courses.add(math) # 小红只选了数学

查询操作
# 查询小明选了哪些课程
student = Student.objects.get(name="小明")
student.courses.all() # <QuerySet [<Course: 数学>, <Course: 英语>]>
  # 查询选了数学的学生
  math = Course.objects.get(name="数学")
  math.students.all() # <QuerySet [<Student: 小明>, <Student: 小红>]>

删除和清空
# 小明退选英语
student.courses.remove(english)
# 小红退选所有课程
s2.courses.clear()

3. 自定义中间表(through)

在这里插入图片描述

如果我们希望记录 学生选课的时间,就需要手动定义中间表:

class StudentCourse
(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
selected_at = models.DateTimeField(auto_now_add=True) # 选课时间
class Meta
:
unique_together = ("student", "course") # 防止重复选课
class Student
(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
courses = models.ManyToManyField(Course, through="StudentCourse", related_name="students")

使用时:

# 小明选数学,并记录时间
StudentCourse.objects.create(student=s1, course=math)
# 查询小明所有选课记录(带时间)
StudentCourse.objects.filter(student=s1)

三、总结

  • ManyToManyField 简化了多对多关系的操作,不需要手动建中间表。
  • 可以使用 add()remove()clear() 来维护关系。
  • 如果需要额外字段,可以通过 through 自定义中间表。
posted @ 2025-09-17 19:22  wzzkaifa  阅读(15)  评论(0)    收藏  举报