一、Django 多對多關系的原理
在關系型數據庫中,多對多關系通常需要 第三張中間表 來維護兩張表之間的對應關系。
在 Django 中,你只需要定義 ManyToManyField
,Django 會自動幫你創建這張中間表。
特點:
- 可以雙向查詢(正向 + 反向)
- 支持添加、刪除、清空操作(
add()
、remove()
、clear()
) - 可以通過
through
參數自定義中間表,添加額外字段(例如時間戳、角色等)
二、實戰案例:學生和課程系統
假設我們有一個 學生(Student) 和 課程(Course) 的關系:
- 一個學生可以選多門課程
- 一門課程可以被多個學生選
1. 定義模型
# models.py
from django.db import modelsclass Course(models.Model):name = models.CharField(max_length=100) # 課程名稱teacher = models.CharField(max_length=100) # 授課老師def __str__(self):return self.nameclass 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
自定義中間表。