ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Django笔记七之ManyToMany和OneToOne介绍

2022-06-25 20:33:39  阅读:150  来源: 互联网

标签:Group OneToOne models ManyToMany Django Person group model name


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 的用法。

标签:Group,OneToOne,models,ManyToMany,Django,Person,group,model,name
来源: https://www.cnblogs.com/lhuan/p/16412344.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有