Django笔记七之ManyToMany和OneToOne介绍
ManyToMany是一种多对多的关系,在用途和使用方法上和外键ForeignKey类似。以下是本篇笔记的目录:ManyToMany的介绍through参数through_field
ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。
以下是本篇笔记的目录:
(资料图片仅供参考)
假设有两个 model,Person 和 Group,这两个model之间是多对多的关系。那么我们可以如下创建其关系:
# blog/models.pyclass 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")
上面 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()
在我们上面创建的 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)
接下来,我们定下最终的几个 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, Membershiphunter = 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.personm1.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")
不同于 多对一和多对多的关系,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.nameclass 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笔记。
原文链接:Django笔记七之ManyToMany和OneToOne介绍
如果想获取更多相关文章,可扫码关注阅读:
标签:
ManyToMany是一种多对多的关系,在用途和使用方法上和外键ForeignKey类似。以下是本篇笔记的目录:ManyToMany的介绍through参数through_field
三月春风暖人心雷锋精神注活力阳春三月,注定与一个温暖的名字相遇—雷锋。无论时代如何变迁,雷锋永远是时代楷模,是一面旗帜
为深入贯彻党的二十大精神,贯彻落实全国教育大会精神和《中国教育现代化2035》、《关于推动现代职业教育高质量发展的意见》等文件精神,充分
1、中文名是:赤霞珠,是一种酿酒葡萄的名字。2、酒标上标注这个词说明这款葡萄酒是由赤霞珠酿成的。3、这种葡萄是酿酒葡萄中名气最大的一种酿
1、杨建群并非是好人,关于王柏林的商业帝国背后的龌龊事件,杨建群和于小卉都有参与。杨建群是北江市公安局刑侦支队支队长,曾是夏远的领导,
白云区气象台解除暴雨橙色预警【II级 严重】
新京报贝壳财经讯(记者姜樊)3月24日,中信银行行长方合英在2022年度业绩发布会上表示,今年中信银行信贷投放计划规模约在3500亿-4000亿元。
越降价越不敢买!2000多家4S店闭店,经销商扛不住了!去库存压力巨大,近百家企业建议:延后国六B执行时间!
[ 相关新闻 ]