当前位置: 首页 > news >正文

Django模型关系:从一对多到多对多全解析

一、一对多关系: ForeignKey

一对多是最常见的模型关系,例如 “作者 - 书籍” 场景:假设一个作者可以写多本书,但每本书只能属于一个作者。

定义关系

核心参数说明:

  • on_delete=models.CASCADE:当作者被删除时,关联的书籍也会被自动删除
  • related_name='books':定义反向查询名称,可通过author.books.all()获取作者的所有书籍
from django.db import modelsclass Author(models.Model):first_name = models.CharField(max_length=100)last_name = models.CharField(max_length=100)def __str__(self):return f"{self.first_name} {self.last_name}"class Book(models.Model):title = models.CharField(max_length=100)publication_date = models.DateField()# 外键关联Author,级联删除,反向查询名为booksauthor = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')def __str__(self):return self.title

数据操作示例

创建数据

# 创建作者
author1 = Author.objects.create(first_name='J.K.', last_name='Rowling')
author2 = Author.objects.create(first_name='George', last_name='Orwell')# 创建书籍并关联作者
book1 = Book.objects.create(title='Harry Potter', publication_date='1997-06-26', author=author1
)
book2 = Book.objects.create(title='1984', publication_date='1949-06-08', author=author2
)

查询操作

# 正向查询:通过书籍找作者
book = Book.objects.get(title='1984')
print(book.author)  # 输出: George Orwell# 反向查询:通过作者找书籍
author = Author.objects.get(last_name='Rowling')
for book in author.books.all():print(book.title)  # 输出: Harry Potter

高级配置

禁用外键约束:当需要灵活管理关联关系(如允许删除存在关联数据的主表记录)时,可关闭数据库级约束

author = models.ForeignKey(Author, on_delete=models.SET_NULL,related_name='books',db_constraint=False,  # 不创建数据库外键约束null=True
)

自定义数据库列名:默认会生成<ClassName>_id列,可通过db_column修改

dept_id = models.ForeignKey("SystemDept",on_delete=models.SET_NULL,db_column="dept_id",  # 显式指定数据库列名null=True
)

二、多对多关系: ManyToManyField

多对多关系适用于 “作者 - 书籍” 的另一种场景:假设一个作者可以写多本书,一本书也可以有多个作者。

定义关系

Django 会自动创建中间表(默认名为appname_book_authors)存储关联关系,无需手动定义。

from django.db import modelsclass Author(models.Model):name = models.CharField(max_length=100)email = models.EmailField()def __str__(self):return self.nameclass Book(models.Model):title = models.CharField(max_length=200)publication_date = models.DateField()# 多对多关联Authorauthors = models.ManyToManyField(Author, related_name='books')def __str__(self):return self.title

数据操作示例

添加 / 移除关联

# 创建实例
author1 = Author.objects.create(name='Alice', email='alice@example.com')
author2 = Author.objects.create(name='Bob', email='bob@example.com')
book = Book.objects.create(title='Example Book', publication_date='2023-01-01')# 添加关联
book.authors.add(author1, author2)# 移除关联
book.authors.remove(author1)

查询操作

# 正向查询:书籍的所有作者
book = Book.objects.get(title='Example Book')
for author in book.authors.all():print(author.name)# 反向查询:作者的所有书籍
author = Author.objects.get(name='Bob')
for book in author.books.all():  # related_name='books'print(book.title)

自定义中间表

当需要存储关联关系的额外信息(如邀请原因、加入时间)时,可自定义中间表

class Membership(models.Model):group = models.ForeignKey(Group, on_delete=models.CASCADE)person = models.ForeignKey(Person, on_delete=models.CASCADE)inviter = models.ForeignKey(Person, related_name="invites", on_delete=models.CASCADE)invite_reason = models.CharField(max_length=64)  # 额外信息class Group(models.Model):name = models.CharField(max_length=128)members = models.ManyToManyField(Person,through="Membership",  # 指定中间表through_fields=("group", "person"),  # 关联字段)

三、性能优化技巧

select_related:用于一对多关系,提前加载关联对象,减少数据库查询

# 普通查询(N+1问题)
entries = Entry.objects.all()
for entry in entries:print(entry.blog.name)  # 每次循环都会触发新查询# 优化后(仅1次查询)
entries = Entry.objects.select_related('blog').all()
for entry in entries:print(entry.blog.name)  # 使用缓存数据

批量操作:利用update()进行批量更新,避免循环操作

# 批量标记站内信为已读
SystemNotifyMessage.objects.filter(id__in=ids.split(",")
).update(read_status=True, read_time=timezone.now()
)

四、关于是否使用外键约束

在实际项目中,是否使用数据库外键约束需要权衡利弊

使用外键的优势

  • 数据完整性:数据库级别的约束保证关联数据一致性
  • 开发效率:ORM 自动处理关联查询和级联操作
  • 查询便捷:支持select_related等优化方法,简化多表查询

禁用外键的场景

  • 高并发系统:外键会增加数据库锁竞争,影响写入性能
  • 分布式架构:分库分表环境下,跨库外键无法生效
  • 复杂迁移:避免循环依赖导致的迁移失败问题

折中方案:使用db_constraint=False 参数

  • 数据库层面:无外键约束,数据库不会强制校验关联数据的存在性
  • Django ORM 层面:保留逻辑关联,ORM仍将字段视为外键关系(逻辑关联),支持 ORM 查询、操作语法
特性db_constraint=True (默认)db_constraint=False
数据库外键约束创建,强制数据一致性不创建
级联操作数据库自动处理仅由 Django ORM 处理
关联数据存在性校验数据库强制校验不校验(需应用层保障)
ORM 查询支持完整支持完整支持(逻辑外键保留)
性能影响外键约束带来额外开销无约束开销
适用场景强数据一致性需求高频写入/跨库/历史数据迁移

五、多对多关系实战

实战场景:在一个后台管理系统中,用户与角色往往是多对多关系。一个用户可以分配多个角色,一个角色也可以属于多个用户。

在这里插入图片描述

模型定义:点击查看完整代码

class SystemUsers(BaseModel, AbstractBaseUser):id = models.BigAutoField(primary_key=True, db_comment="用户ID", help_text="用户ID")username = models.CharField(max_length=30, unique=True, db_comment="用户账号", help_text="用户账号")# ...# 与角色多对多关系roles = models.ManyToManyField("SystemRole",through="SystemUserRole",through_fields=("user_id", "role_id"),related_name="users",)# ...    class SystemUserRole(BaseModel):"""用户和角色关联中间表"""id = models.BigAutoField(primary_key=True, db_comment="id")user_id = models.ForeignKey("SystemUsers",on_delete=models.CASCADE,db_constraint=False,db_column="user_id",db_comment="用户ID",)role_id = models.ForeignKey("SystemRole",on_delete=models.CASCADE,db_constraint=False,db_column="role_id",db_comment="角色ID",)class Meta:managed = Truedb_table = "system_user_role"db_table_comment = "用户和角色关联表"ordering = ["-id"]

system_user_role数据库生成的中间表

在这里插入图片描述


您正在阅读的是《Django从入门到实战》专栏!关注不迷路~

http://www.dtcms.com/a/306146.html

相关文章:

  • JSON.parse解析大整数踩坑
  • 9.项目起步(3)
  • ETCD学习之路
  • 代码随想录算法训练营第三十六天
  • 微软 Power Platform 使用Power Automate自动添加用户到AD域环境并分配环境角色
  • 「源力觉醒 创作者计划」_文心大模型 4.5 开源 28 天:从车间轴承到山村课堂的 AI 突围
  • 硬件电路设计(基本元器件)
  • 微信小程序苹果手机和安卓,怎么做适配
  • 链游与传统游戏的区别
  • 2025年自动化工程与计算机网络国际会议(ICAECN 2025)
  • TypeScript:前端语言,后端哲学
  • 桶排序-Java实现
  • 关于Docker【常见问题解决方案】
  • 数学建模——01规划/整数规划
  • sql developer 中文显示问号 中文显示乱码 错误消息显示问号
  • Shopify Draggable + Vue 3 完整指南:打造现代化拖拽交互体验
  • 小程序端基于 AI 的语音交互功能深度开发
  • Spring AI 海运管理应用
  • Webstorm 和 Intellij Idea 最新版 Git 本地修改丢失,手工开启 git 的 Local Changes
  • APM32芯得 EP.27 | 告别IDE,为APM32F411打造轻量级命令行开发工作流
  • socket网络编程(1)
  • 基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(五)
  • C语言数据结构(1)顺序表专题2.顺序表的应用
  • YOLO融合MogaNet中的ChannelAggregationFFN模块
  • LeetCode 53 - 最大子数组和
  • 明智运用C++异常规范(Exception Specifications)
  • AI 驱动的软件测试革新:框架、检测与优化实践
  • 洛谷刷题7.30
  • 【力扣热题100】哈希——最长连续序列
  • Redis知识点(2)