Django 中的多对多关系(ManyToManyField)三种定义方式

发布于:2025-06-26 ⋅ 阅读:(21) ⋅ 点赞:(0)

Django 中的多对多关系(ManyToManyField)可以通过 三种方式定义,它们在使用便捷性和可扩展性上各有差异。以下是完整总结 ✅ 并注明每种方式中哪些方法不可用


✅ 方式一:默认方式(Django 自动创建中间表)

✅ 特点:

  • 最常用、最方便

  • Django 自动生成中间表

  • 不支持自定义字段

✅ 示例:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)

✅ 可用方法:

方法 可用? 说明
.add(obj) 添加关联
.remove(obj) 移除关联
.set([obj1, obj2]) 重新设置关联
.clear() 清除所有关联
.all() 查询所有关联对象

✅ 方式二:使用 through 自定义中间表(推荐用于需要额外字段的情况)

✅ 特点:

  • 中间模型可添加自定义字段(如创建时间、角色等)

  • 更灵活,但略复杂

✅ 示例:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(
                            Author, 
                            through="BookAuthor", 
                            through_fields=("book", "author")
)

class BookAuthor(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    added_at = models.DateTimeField(auto_now_add=True)

❌ 禁用方法:

方法 可用? 说明
.add(obj) 被禁用,Django 不知道如何填中间表
.remove(obj) ❌ 同上
.set([obj1, obj2]) ❌ 同上
.clear() ❌ 同上
.all() 查询仍然可用

✅ 正确操作方式:

使用中间模型手动添加:

BookAuthor.objects.create(book=book, author=author)

✅ 方式三:完全手动中间表,无 ManyToManyField

✅ 特点:

  • 不定义 ManyToManyField

  • 所有关系自己通过中间表查询管理

  • 最灵活,但也最复杂,不推荐日常使用

✅ 示例:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)

class BookAuthor(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    added_at = models.DateTimeField(auto_now_add=True)

❌ 所有 .authors.xxx 都不可用

方法 可用? 说明
.add() / .remove() / 等 没有 ManyToManyField 字段
.all() 也不能直接访问 .authors.all()
查询方式 需手动通过中间表查询
# 查询一本书的所有作者
BookAuthor.objects.filter(book=book).select_related("author")

# 查询一个作者的所有书
BookAuthor.objects.filter(author=author).select_related("book")

✅ 总结对比表

方式 是否有中间模型 是否能添加中间字段 快捷方法(add/remove/set) 推荐用途
默认 ManyToManyField ❌(Django 自动) ✅ 可用 简单多对多关系
ManyToManyField + through ❌ 被禁用 多对多且需附加字段场景
完全手动中间模型 ❌ 全部禁用 高度自定义业务、底层控制场景

在 Django 中,ManyToManyField 提供了一套专门用于 增删改查(CRUD) 的方法,用于操作模型之间的多对多关系。以下是完整的 CRUD 方法总结(适用于未使用 through 中间模型的情况):


✅ Django 多对多的 CRUD 方法汇总

假设模型如下:

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)

✅ 1. C:创建关系(Create)

➤ 方法一:创建对象后添加关联

book = Book.objects.create(title="Python入门")
author = Author.objects.create(name="张三")

book.authors.add(author)  # 添加关联关系

➤ 方法二:一次添加多个对象

book.authors.add(author1, author2, author3)

✅ 2. R:查询关系(Read)

# 查询某本书的所有作者
book.authors.all()

# 查询某个作者参与的所有书(反向查询)
author.book_set.all()

# 如果设置了 related_name="books",可以用:
author.books.all()

✅ 3. U:更新关系(Update)

➤ 重新设置关系(会先清空原有关系再添加新关系)

book.authors.set([author1, author2])  # 只保留这两个作者

✅ 4. D:删除关系(Delete)

➤ 删除指定的某个或多个关系:

book.authors.remove(author1)
book.authors.remove(author1, author2)

➤ 清除所有关系:

book.authors.clear()

✅ 使用限制说明(重要)

方法 是否可用 条件说明
.add() 不能用于通过 through 自定义中间模型
.remove() 同上
.clear() 同上
.set() 同上
.all() 始终可用

❗ 如果你在 ManyToManyField 中使用了 through="XXX",上述 .add() 等方法会全部失效,必须手动操作中间模型。


✅ 5. 示例:完整流程演示

# 添加关系
book = Book.objects.create(title="FastAPI进阶")
author1 = Author.objects.create(name="Alice")
author2 = Author.objects.create(name="Bob")
book.authors.add(author1, author2)

# 查询关系
for author in book.authors.all():
    print(author.name)

# 更新关系
book.authors.set([author2])  # 现在只有 Bob

# 删除单个关系
book.authors.remove(author2)

# 清空所有关系
book.authors.clear()


网站公告

今日签到

点亮在社区的每一天
去签到