一、一对多关系: ForeignKey

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

定义关系

核心参数说明:

  • on_delete=models.CASCADE:当作者被删除时,关联的书籍也会被自动删除
  • related_name='books':定义反向查询名称,可通过author.books.all()获取作者的所有书籍
from django.db import models

class 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,级联删除,反向查询名为books
author = 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 models

class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField() def __str__(self):
return self.name class Book(models.Model):
title = models.CharField(max_length=200)
publication_date = models.DateField()
# 多对多关联Author
authors = 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 = True
db_table = "system_user_role"
db_table_comment = "用户和角色关联表"
ordering = ["-id"]

system_user_role数据库生成的中间表


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

Django模型关系:从一对多到多对多全解析的更多相关文章

  1. Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作

    Python进阶----表与表之间的关系(一对一,一对多,多对多),增删改查操作,单表查询,多表查询 一丶表与表之间的关系 背景: ​ ​ ​  ​ ​ 由于如果只使用一张表存储所有的数据,就会操作数 ...

  2. django 模型关系

    模型关系 关系数据库的威力体现在表之间的相互关联,Django提供了三种最常见的数据库关系:多对一 (many-to-one),多对多(many-to-many),一对一(one-to-one) 多对 ...

  3. Nhibernate 映射关系,一对多 多对一与多对手在映射文件中的体现。

    今天做了第一个Nhibernate项目,摸着石头过河,学到了一些东西,在这里将自己总结体会到的一些映射关系写出来,与大家分享,由于是初学者,如果有不对的地方希望大家能够指出来. 首先要说明要建立的几张 ...

  4. Django模型层—ORM

    目录 一.模型层(models) 1-1. 常用的字段类型 1-2. 字段参数 1-3. 自定义char字段 1-4. 外键关系 二.Django中测试脚本的使用 三.单表操作 3-1. 添加记录 3 ...

  5. 八.django模型系统(二)之常用查询及表关系的实现

    Ⅰ.常用查询  1.几个概念 每一个django模型类,都有一个默认的管理器,objects,查询就是依赖于objects管理器进行的(在创建时就被添加了). QuerySet表示数据库中对象的列表( ...

  6. hibernate中一对多多对一关系设计的理解

    1.单向多对一和双向多对一的区别? 只需要从一方获取另一方的数据时 就使用单向关联双方都需要获取对方数据时 就使用双向关系 部门--人员 使用人员时如果只需要获取对应部门信息(user.getdept ...

  7. Django 08 Django模型基础3(关系表的数据操作、表关联对象的访问、多表查询、聚合、分组、F、Q查询)

    Django 08 Django模型基础3(关系表的数据操作.表关联对象的访问.多表查询.聚合.分组.F.Q查询) 一.关系表的数据操作 #为了能方便学习,我们进入项目的idle中去执行我们的操作,通 ...

  8. django模型层之多表关系

    一. 多表操作 数据库表关系之关联字段与外键约束 一对多 book(多) publish(一) 查询<<水浒传>>这本书出版社的地址: select publish_id fr ...

  9. django模型中有外键关系的表删除相关设置

    0904自我总结 django模型中有外键关系的表删除相关设置 一.一对一 例如有Author.AuthorDetail两表 author = models.OneToOneField(to='Aut ...

  10. django模型中的关系对应

    显然,关系数据库的力量在于将表相互关联.Django提供了定义三种最常见的数据库关系类型的方法:多对一,多对多和一对一. 在说明之前,首先来理解一下这三个概念: 多对一: 两个集合a,b;集合a中的多 ...

随机推荐

  1. 操作系统:设备I/O -- 设备如何处理内核I/O包

    上一讲实现了建立设备的接口,相当于制定了部门的相关法规,只要遵循这些法规就能建立一个部门.一个部门的职责不难确定,它应该能对上级下发的任务作出响应,并完成相关工作,而这对应到设备,就是如何处理内核的I ...

  2. python存储MongoDB数据

    MongoDB是由C++语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档.数组及文档数组,非常灵活(总体来看,python ...

  3. 为什么使用MQ

    在项目中,可将一些无需即时返回且耗时的操作提取出来,进行异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量. 开发中消息队列通常有如下应用场景: 1.任务异步处理 ...

  4. 转-Spring Data JPA中对象属性自动更新数据库

    摘要:使用Spring Data JPA获取的对象,其属性变更后自动更新数据库问题排查与解决方案. §问题描述   使用继承了JpaRepository的Dao从数据库中获取到某个对象,然后操作这个对 ...

  5. Python 潮流周刊#104:Python 考虑添加虚拟线程啦?(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  6. java list<对象>根据某个字段分组

    前言 仅供学习参考,不保证性能问题 其中的实体类改成你自己的实体类 代码 /** * 根据某个字段进行分组,分组后遍历方法 * <p> * Map<String, List<M ...

  7. 真正的生产力来了!Docker迁移部署两步搞定!

    前言 最近遇到了需要部署一套比较复杂的应用场景,刚好这套应用我在其他服务器部署过,为了节省折腾的时间,我打算直接把服务器上已有的搬过去. PS:没想到这个过程比从头开始来耗费时间 好在是把一键迁移的脚 ...

  8. AI应用实战课学习总结(10)用CNN做图像分类

    大家好,我是Edison. 最近入坑黄佳老师的<AI应用实战课>,记录下我的学习之旅,也算是总结回顾. 今天是我们的第10站,一起了解CNN卷积神经网络 以及 通过CNN做图像分类任务的案 ...

  9. ChatGPT学习之旅 (3) Prompt进阶用法

    大家好,我是Edison. 上一篇:Hello Prompt 复习Prompt用法 还记得上一篇学到的黄金公式吗? 这里,我们先来复习一下,假如我们想要ChatGPT来扮演一个[私人营养师]为我们给出 ...

  10. SQL 语句生成MD5值

    https://blog.csdn.net/BowenXu11/article/details/104627263 select substring(sys.fn_sqlvarbasetostr(Ha ...