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自定义中间表。

浙公网安备 33010602011771号