首页 > 其他分享 >Django笔记七之ManyToMany和OneToOne介绍

Django笔记七之ManyToMany和OneToOne介绍

时间:2023-03-24 22:22:21浏览次数:53  
标签: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 的用法。

本文首发于本人微信公众号:Django笔记。

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

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

标签:Group,OneToOne,models,ManyToMany,Django,Person,group,model,name
From: https://www.cnblogs.com/hunterxiong/p/17253517.html

相关文章

  • Django 大数据 orm 操作 - 报错及解决方法记录
    报错:django.db.utils.OperationalError:(1153,"Gotapacketbiggerthan'max_allowed_packet'bytes")解决方法:修改mysql配置文件的max_allowed_packet配置参数......
  • Centos + Django + Nginx + uwsgi 部署项目-rpm包安装 Mysql 5
    笔者发觉下面这个方法可能有些缺陷,适合自己的就看下,如果是新开的虚拟机有可能不适用下面的方法,云服务器开的LinuxCentos系统应该可以。虚拟机安装Mysql的具体方法,可以看......
  • django -表单 forms 类的使用 - Form 和 ModelForm - 时间格式的配置
    @目录表单是搜集用户数据信息的各种表单元素的合集,其作用是实现网页上的数据交互,比如用户在网站输入数据信息,然后提交到网站服务器进行处理(如数据录入和用户登陆注册),分为......
  • Python 栈 django框架开源商城系统baykeShop
    起始2022年我用django开发了一个商城的第三方包,起名为:django-happy-shop。当时纯粹是利用业余时间来开发和维护这个包,想法也比较简单,Python语言做web可能用的人比较少,不一......
  • django-视图
    请求HttpRequest对象常用request属性GET:数据类型时一个类似字典的对象,包含HTTPGET的所有参数get():返回字符串,多个值取出最后一个request.GET.get(......
  • Django笔记六之外键ForeignKey介绍
    这一篇笔记介绍Django系统model的外键处理,ForeignKey以及相应的处理方法。这是一种一对多的字段类型,表示两张表之间的关联关系。本篇笔记的目录如下:on_deleterel......
  • django-admin常用知识点记录
    1,格式化时间defshowtime(self): returnself.create_time.strftime('%Y/%m/%d')showtime.short_description="创建时间" #更改列名,否则显示的是showtime名称showtime......
  • django-admin 隐藏或禁用按钮
    1.屏蔽添加按钮:defhas_add_permission(self,request):returnFalse2.屏蔽自定义按钮如果不是超级管理员或者不是运营部的,则不显示对应的自定义按钮defget_......
  • Django生成验证码图片
    1、安装和准备pipinstallpillow准备一款字体重命名为【Monaco.ttf】放在项目的根目录下2、生成验证码图片代码importrandomfromPILimportImage,ImageDraw,Ima......
  • Django笔记五之字段类型
    这篇笔记介绍字段的类型FieldType。Django的model下的field对应的是MySQL中的表字段,而我们定义的field的类型则对应MySQL中的字段类型。本次主要介绍以下几......