本文首发于本人微信公众号: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. STM32CubeMX教程15 ADC - 多重ADC转换

    1.准备材料 开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK ...

  2. 【网络爬虫学习】Python 爬虫初步

    本系列基于 C语言中文网的 Python爬虫教程(从入门到精通)来进行学习的, 部分转载的文章内容仅作学习使用! 前言 网络爬虫又称网络蜘蛛.网络机器人,它是一种按照一定的规则自动浏览.检索网页信息的 ...

  3. Educational Codeforces Round 93 (Rated for Div. 2)

    Educational Codeforces Round 93 (Rated for Div. 2) A. Bad Triangle input 3 7 4 6 11 11 15 18 20 4 10 ...

  4. POJ - 2259 Team Queue (队列)

    题目链接 https://vjudge.net/problem/POJ-2259 题解 在任何时刻,同一个小组的人只要来到了队伍,就会站在一起,所以我们建立一个队列q0存储队伍中所有小组的编号,再为每 ...

  5. 【转载】内存基本概念-watermark&lowmem_reserve

    概述 当系统内存短缺的情况下仍去申请内存,可能会触发系统对内存的回收,那什么时候应该进行回收,回收到什么标准又可以停止回收,参考依据是什么?即本文将介绍的watermark(内存水位线),当检查wat ...

  6. Linux环境下如何查看Python版本号

    方法一.直接执行命令python,就可以查看python的版本信息. 退出用exit() 方法二.利用命令python -V,注意V要大写. 方法三.利用命令whereis python,注意wher ...

  7. 【收集】embed

    序 链接 备注 1 适用于嵌入式单片机的差分升级通用库+详细教程_风吹花中花吹风的博客-CSDN博客 差分升级包制作与还原 2 前言 · 给新手的两轮自平衡小车开发实战指南 (miaowlabs.co ...

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

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

  9. [转帖]tikv性能参数调优

    https://www.cnblogs.com/FengGeBlog/p/10278368.html#:~:text=max-%20bytes%20-for-level-%20base%20%3D%2 ...

  10. [转帖]Intel AVX 系列指令基础介绍

    https://zhuanlan.zhihu.com/p/437657452 一.发展背景 1993年,Intel公司推出了奔腾处理器,该类型处理器拥有两条执行流水线,和当时的处理器相比,可以同时执行 ...