本文首发于本人微信公众号:Hunter后端

原文链接:Django笔记七之ManyToMany和OneToOne介绍

ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。

以下是本篇笔记的目录:

  1. ManyToMany 的介绍
  2. through 参数
  3. through_fields 参数
  4. ManyToMany关系数据的增删改查
  5. OneToOne介绍

1、ManyToMany 的介绍

假设有两个 model,Person 和 Group,这两个model之间是多对多的关系。那么我们可以如下创建其关系:

# blog/models.py

class Person(models.Model):
name = models.CharField(max_length=64) class Group(models.Model):
name = models.CharField(max_length=64)
members = models.ManyToManyField(Person)

通过上述代码我们就创建了有多对多关系两个model,当我们执行 migrate 操作之后(可以先不执行,后续还会对其有所更改),系统除了会创建 Person 和 Group 这两个表之外,还会创建一个表。

表名为 blog_group_members,因为是放在 Blog 这个 application 下面,所以,表名的前缀是 blog,然后加上 model 名的小写为 group,加上字段名称 members。

这张第三方表会有两个字段,person_id 和 group_id,将这两个 model 关联起来。

通过往这张第三方表写入 person_id 和 group_id的数据,我们就将这两个 model 关联了起来。

而获取他们对应的关系的记录和 ForeignKey 的关系类似:

根据 Person 数据查询关联的 Group 数据:

person = Person.objects.get(id=1)
group_list = person.group_set.all() # 使用 Group 的小写加 _set

根据 Group 数据查询关联的 Person 数据,这个查询方法略微不同,用到的是 Group 定义的 members 字段:

group = Group.objects.get(id=1)
person = group.members.all() # 根据条件来搜索 person 也是可以的
person = group.members.filter(name='hunter')

2、through参数

上面 ManyToMany 的定义中,我们没有加任何参数,所以自动创建的表名是默认的,字段也只是两个 model 的主键id。

而如果我们有一些额外的需求,比如说,为 Person 和 Group 添加关联关系时,需要加上关联时间,或者想自己指定表名或 model 名的时候,我们可以通过 through 属性来指定 model 的名称,然后为其添加我们需要的字段。

比如我们想为 Person 和 Group 创建一个多对多的关系,指定model 名为 Membership,且额外添加字段,比如添加时间,可以通过 through 参数来指定:

class Person(models.Model):
name = models.CharField(max_length=50) class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
) class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()

3、through_fileds参数

在我们上面创建的 Membership model 中,我们对应的多对多的字段分别是 person 和 group,所以系统可以自动找到对应的多对多的字段。

如果在第三方表,也就是 Membership 中,有多个相同的 Person 或者 Group 的字段,就需要通过 through_fields 参数来指定多对多的字段:

class Person(models.Model):
name = models.CharField(max_length=50) class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
) class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)

4、ManyToMany关系数据的增删改查

接下来,我们定下最终的几个 model 内容如下,用于演示 ManyToMany 的增删改查的操作:

class Person(models.Model):
name = models.CharField(max_length=50) class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
) class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)

现在我们有 Person 和 Group 两个model,还有两个 model 之间的关系表 Membership,如果我们要创建一个对应的关系,则需要创建一个 Membership 实例。

创建

首先创建 Person 和 Group 的记录:

from blog.models import Person, Group, Membership
hunter = Person.objects.create(name='hunter')
group_1 = Group.objects.create(name='group_1')

创建多对多记录:

m1 = Membership.objects.create(person=hunter, group=group_1, date_joined='2022-01-01', invite_reason='xxx')

根据单个 Person 记录获取所有相关 Group 记录,使用方法同外键搜索方法:

groups = hunter.group_set.all()

根据单个 Group 记录获取所有相关 Person 记录,根据多对多字段来搜索:

persons = group_1.members.all()

根据 Membership 关系记录获取 Person 和 Group 记录,可以直接用外键的的方式使用:

m1.person
m1.group

根据 Group 使用 add 添加一条多对多记录:

paul = Person.objects.create(name='pual')
group_1.members.add(paul, through_defaults={'date_joined': '2022-01-01'})

其中,through_defaults 参数为字典,内容为多对多关系表的额外添加的字段。

根据 Group 使用 create 创建一条多对多记录:

如果没有相应的 Person 记录,可以根据 Group 来直接创建

group_1.members.create(name='mary', through_defaults={'date_joined': '2022-01-01'})

根据 Group 使用 set 刷新多对多记录:

使用 set 方法来设置多对多的关系:

jack = Person.objects.create(name='jack')
lucy = Person.objects.create(name='lucy')
group_1.members.set([jack, lucy], through_defaults={'date_joined': '2022-01-01'})

需要注意的是,使用 set() 方法加上关联之后,这个 Group 实例之前设置的关联数据都会被清除。

也就是说,set() 里设置的关联数据就是最终所有的关联数据。

根据 Group 使用 remove 删除一条多对多记录:

在上面 我们使用了 set() 方法设置了两条关联数据,jack 和 lucy,现在我们想要把 jack——group_1 这条关系删除,可使用 remove() 方法:

group_1.members.remove(jack)

使用 clear 清除某个 Group 实例上所有关系:

group_1.members.clear()

多对多的搜索:

根据 Person 的条件搜索 Group 的数据:

比如搜索 Person 的 name 字段为 'hunter' 的 Group 数据

Group.objects.filter(members__name='hunter')

根据 Group 的条件搜索 Person 的数据:

比如搜索 Group 的 name 字段为 'group_1' 的Person关联数据:

Person.objects.filter(group__name='group_1')

如果要搜索额外的关联字段:

Person.objects.filter(group__name='group_1', membership__date_joined='2022-01-01')

5、OneToOne 介绍

不同于 多对一和多对多的关系,OneToOne 是一对一的关系,也就是说 一条数据仅能被另一条数据关联。

下面是两个 OneToOne 对应的 model:

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80) def __str__(self):
return "%s the place" % self.name class Restaurant(models.Model):
place = models.OneToOneField(Place, on_delete=models.CASCADE, default=None, related_name='place_restaurant', null=True)

接下来创建两条数据:

r_1 = Restaurant.objects.create()
p_1 = Place.objects.create(name='beijing', address='beijing')

根据 Restaurant 获取 Place 数据,直接根据字段获取数据:

r_1.place

如果根据 Place 获取 Restaurant 数据,因为是 OneToOne 的关系,所以可以直接获取:

上面的 model 中我们定义了 related_name,所以是:

p_1.place_restaurant

如果没有定义 related_name 和 related_query_name 那么就是 model 的小写:

p_1.restaurant

但是从 Place 到 Restaurant 获取数据,如果没有这种 OneToOne 的对应,比如我们上面直接创建的 p_1,使用这种方式获取关联数据就会报错,因为没有这种 OneToOne 的数据。

那么这个时候我们可以先判断是否有对应的这种 OneToOne 的数据:

hasattr(p_1, 'place_restaurant')  # 返回的是布尔型数据 

# 或者
getattr(p_1, 'place_restaurant', None)

以上就是这篇笔记全部内容,接下来将要介绍 model 里的 Meta 的用法。

如果想获取更多相关文章,可扫码关注阅读:

Django笔记七之ManyToMany和OneToOne介绍的更多相关文章

  1. Django笔记十九之manager用法介绍

    本文首发于微信公众号:Hunter后端 原文链接:Django笔记十九之manager用法介绍 首先介绍一下 manager 的使用场景,比如我们有一些表级别的,需要重复使用的功能,都可以使用 man ...

  2. Django笔记二十九之中间件介绍

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十九之中间件介绍 这一节介绍一下 Django 的中间件. 关于中间件,官方文档的解释为:中间件是一个嵌入 Django 系统的 req ...

  3. Django笔记六之外键ForeignKey介绍

    这一篇笔记介绍 Django 系统 model 的外键处理,ForeignKey 以及相应的处理方法. 这是一种一对多的字段类型,表示两张表之间的关联关系. 本篇笔记的目录如下: on_delete ...

  4. 第三百七十八节,Django+Xadmin打造上线标准的在线教育平台—django自带的admin后台管理介绍

    第三百七十八节,Django+Xadmin打造上线标准的在线教育平台—django自带的admin后台管理介绍 配置django的admin数据库管理后台 首先urls.py配置数据库后台路由映射,一 ...

  5. Django笔记&教程 7-1 基于类的视图(Class-based views)介绍

    Django 自学笔记兼学习教程第7章第1节--基于类的视图(Class-based views)介绍 点击查看教程总目录 1 介绍 Class-based views (CBVs) are view ...

  6. Django笔记五之字段类型

    这篇笔记介绍字段的类型 Field Type. Django 的model 下的 field 对应的是 MySQL 中的表字段,而我们定义的 field 的类型则对应 MySQL 中的字段类型. 本次 ...

  7. Django笔记十一之外键查询优化select_related和prefetch_related

    本篇笔记目录如下: select_related prefetch_related 在介绍 select_related 和 prefetch_related 这两个函数前,我们先来看一个例子. 对于 ...

  8. Django笔记 —— 模型

    最近在学习Django,打算玩玩网页后台方面的东西,因为一直很好奇但却没怎么接触过.Django对我来说是一个全新的内容,思路想来也是全新的,或许并不能写得很明白,所以大家就凑合着看吧- 本篇笔记(其 ...

  9. Django笔记&教程 7-3 拓展CBVs(Class-based views)

    Django 自学笔记兼学习教程第7章第3节--拓展CBVs(Class-based views) 点击查看教程总目录 一般而言,直接使用原生的Class-based views,能展现的样式和内容是 ...

  10. Django笔记二十八之数据库查询优化汇总

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十八之数据库查询优化汇总 这一篇笔记将从以下几个方面来介绍 Django 在查询过程中的一些优化操作,有一些是介绍如何获取 Django ...

随机推荐

  1. 数据存入已有Excel

    import openpyxl filepath = r'.\UCB2Create_course\SuccsessCourse.xlsx' wb = openpyxl.load_workbook(fi ...

  2. C#9.0:Top-Level Programs

    我们称之为顶级层序 用 C# 编写一个简单的程序需要大量的样板代码,引用,类.方法.结构体等: 1 class Program 2 { 3 static void Main(string[] args ...

  3. HHKB 键盘布局记录以及一些闲言碎语

    HHKB (happy hacking keyboard) 是世界顶级键盘品牌,自 1996 年推出以来畅销至今.与其他键盘不同,HHKB 机身小巧,省略了 F1 - F12 功能键.光标键和 Pag ...

  4. 森林消防智慧预警:火灾监测 Web GIS 可视化平台

    前言 森林火灾是一种突发性强.破坏性大.处置救助较为困难的自然灾害.2021 年前三季度全国共发生森林火灾 527 起,受害森林面积约 2628 公顷,15 人死亡:发生草原火灾 12 起,受害草原面 ...

  5. SAE 联合乘云至达与谱尼测试携手共同抗疫

    作者 | 营火.计缘.张祖旺 前言 当前疫情形势依然严峻,各行各业众志成城,携手抗疫.新冠病毒核酸检测筛查是疫情防控的重要一环,如何应对疫情的不断反复,以及每日数以万计的核酸检测结果成为每个检测公司的 ...

  6. 一、java发送http的各类请求

    导航 一.java发送http的各类请求 二.java发送https的各类请求 java开发中需要调用其他服务的对外提供的http请求可以参考如下代码: 注:调用的主类比较简单就不写了. pom.xm ...

  7. Git | git branch 分支操作

    假设我们已经有了稳定的代码,现在我想整一些花活.比较安全的一个方式是,在新的分支上整活. 新建 vga 分支:git branch vga,然后切换到 vga 分支:git switch vga,或者 ...

  8. jvisualm 结合 visualGC 进行jvm监控,并分析垃圾回收

    本文为博主原创,未经允许不得转载 1.jvisualm 的使用 打开 jdk 安装目录bin目录下的 jvisualvm.exe 工具 2. visual GC插件的安装及监控分析 Visual GC ...

  9. 当ChatGPT遇上了CoT

    最近在看CoT(Chain-of-Thought,思维链)方面的论文<Chain-of-Thought Prompting Elicits Reasoning in Large Language ...

  10. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.12.2)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...