django与python之间关系_说说 Python Django 模型之间的多对多关系

使用 django.db.models.ManyToManyField 类,就可以定义出一个多对多的关联关系。与 ForeignKey 类用法相同,也是在模型中,添加一个值,作为ManyToManyField 类的实例,并且也有一个入参,用于定义想要关联的模型类名。

1 定义模型

例如:一本书可以被定义为多个标签,而一个标签也可以属于多本书,所以书与标签之间属于多对多关系。

在 models.py 中,新建标签与书模型类:

'''

标签

'''

class Tag(models.Model):

name = models.CharField(max_length=50)

def __str__(self):

return '%s' % (self.name)

'''

'''

class Book(models.Model):

...

tags = models.ManyToManyField(Tag)

...

执行 migrate 迁移命令成功后,就会创建一个新的关系表 chart_book_tags,用于实现多对多关系:

这张关系表建了自有主键,并定义了两个外键,分别对应所需要关联的两张表的主键:

因为一般来说,会在书模型中,看到它所隶属的标签。所以,我们把 ManyToManyField 字段定义在 Book 模型中。

2 使用方法

2.1 新建

2.1.1 add 方法

首先新建标签模型并保存:

>>> from chart.models import *

>>> tag1=Tag(name='治愈')

>>> tag1.save()

>>> tag2=Tag(name='温暖')

>>> tag2.save()

然后,创建图书模型:

book=Book(name='解忧杂货店')

注意: 比如先保存图书模型,才能关联标签模型。否则会抛出 ValueError 错误:

ValueError: Cannot add "": instance is on database "None", value is on database "default"

保存图书模型后,再关联相应的标签:

>>> book1=Book(name='解忧杂货店')

>>> book1.save()

>>> book1.tags.add(tag1)

>>> book1.tags.add(tag1,tag2)

从库表中,可以看到关联关系已经建立好了:

可以多次关联同一关系,但这没有意义,因为并不会对库表关系造成任何影响:

>>> book1.tags.add(tag1,tag2)

如果关联的模型不是定义的那样,就会抛出 TypeError,比如:

book1.tags.add(book1)

TypeError: 'Tag' instance expected, got

还可以从标签对象中,通过 add 方法创建图书的同时,建立关联关系:

>>> tag3=Tag(name='科幻')

>>> tag3.save()

>>> book3=Book(name='海底两万里')

>>> book3.save()

>>> tag3.book_set.add(book3)

>>> book3.tags.all()

]>

2.1.2 create 方法

也可以直接在 Book 模型的 tags 中,调用 create 方法,直接创建标签:

>>> book1.tags.create(name='小说')

在标签对象中,也可以通过 create 方法关联图书:

>>> tag3.book_set.create(name='呼吸')

>>> tag3.book_set.all()

, ]>

>>> book4=tag3.book_set.all()[1]

>>> book4.tags.all()

]>

2.1.3 set 方法

tags 除了 create 方法,还可以利用 set 方法来建立关系:

>>> book3.tags.all()

>>> book3.tags.set([tag3])

>>> book3.tags.all()

]>

2.2 查询

从 Book 对象访问 Tag 对象:

>>> book1.tags.all()

, , ]>

从 Tag 对象访问 Book 对象:

>>> tag1.book_set.all()

]>

也可以以 Tag 对象作为条件,查询出所对应的 Book 对象:

>>> Book.objects.filter(tags=1)

]>

>>> Book.objects.filter(tags=tag1)

]>

>>> Book.objects.filter(tags__name__startswith='温')

]>

>>> Book.objects.filter(tags__name__startswith='温').distinct()

]>

支持 count()、in 语法:

>>> Book.objects.filter(tags__name__startswith='温').count()

1

>>> Book.objects.filter(tags__in=[1, 2])

, ]>

>>> Book.objects.filter(tags__in=[tag1, tag2])

, ]>

>>> Book.objects.filter(tags__in=[tag1, tag2]).distinct()

]>

还支持反向查询,即以 Book 对象作为条件,查询出所对应的 Tag 对象:

>>> Tag.objects.filter(id=1)

]>

>>> Tag.objects.filter(pk=1)

]>

>>> Tag.objects.filter(book__name__startswith='解')

, , ]>

>>> Tag.objects.filter(book='解忧杂货店')

, , ]>

>>> Tag.objects.filter(book=book1)

, , ]>

>>> Tag.objects.filter(book__in=['解忧杂货店','西中有东']).distinct()

, , ]>

>>> Tag.objects.filter(book__in=[book1,'西中有东']).distinct()

, , ]>

排除掉某些标签:

>>> Book.objects.exclude(tags=tag1)

, ]>

2.3 删除

2.3.1 delete 方法

删除某个标签后,图书实例的 tags 中也就查询不到:

>>> book1.tags.all()

, , ]>

>>> tag1.delete()

(2, {'chart.Book_tags': 1, 'chart.Tag': 1})

>>> book1.tags.all()

, ]>

与此类似,删除了某个图书后,从标签的 book_set 中就查询不到啦。

delete 方法还可以用于批量删除:

>>> tag4=Tag(name='科技')

>>> tag4.save()

>>> Tag.objects.all()

, , , ]>

>>> Tag.objects.filter(name__startswith='科').delete()

(2, {'chart.Book_tags': 0, 'chart.Tag': 2})

>>> Tag.objects.all()

, ]>

2.3.2 remove 方法

也可以通过 A 实例来移除所关联的 B 实例:

>>> tag3.book_set.all()

, ]>

>>> book3.tags.all()

]>

>>> book3.tags.remove(tag3)

>>> tag3.book_set.all()

]>

>>> book3.tags.all()

与此类似,从 Tag 实例的 book_set 中也可以利用 remove() 方法移除 Book 实例。

2.3.3 clear 方法

利用 clear 方法,可以解除关联关系:

>>> tag3.book_set.all()

, ]>

>>> tag3.book_set.clear()

>>> tag3.book_set.all()

与此类似,从 Book 实例的 tags 对象中,也可以调用 clear 方法,来解除关联关系。

3 自定义关联模型

3.1 定义模型

默认的多对多关联模型,只有三个字段,它们分别是 ID以及所关联的两个模型的 ID。有时候,我们需要在关联模型中,记录更多的信息。比如公园游乐设施管理,一种游乐设施被加入公园时,希望记录它加入的时间以及加入缘由。

'''

游乐设施

'''

class Recreation_Facility(models.Model):

name = models.CharField(max_length=50)

def __str__(self):

return '%s' % (self.name)

'''

公园

'''

class Park(models.Model):

name = models.CharField(max_length=50)

relations = models.ManyToManyField(Recreation_Facility, through='Park_Facility_Relations')

def __str__(self):

return '%s' % (self.name)

'''

公园与游乐设施之间的关联关系

'''

class Park_Facility_Relations(models.Model):

recreation_facility = models.ForeignKey(Recreation_Facility, on_delete=models.CASCADE)

park = models.ForeignKey(Park, on_delete=models.CASCADE)

joined_date = models.DateField()

invite_reason = models.CharField(max_length=300)

执行 migrate 指令之后,就可以在数据库中看到建好的自定义关系表:

自定义关联模型必须有且仅有一个指向所需关联模型的 ForeignKey,即一个模型一个外键。

3.2 使用模型

因为是自定义的关联模型,所以必须自行编写代码,初始化关联模型实例,并保存:

>>> from datetime import date

>>> rotating_horse=Recreation_Facility.objects.create(name='转马')

>>> fly_chair=Recreation_Facility.objects.create(name='飓风飞椅')

>>> swing_hammer=Recreation_Facility.objects.create(name='大摆锤')

>>> park=Park.objects.create(name='南京公园')

>>> r1=Park_Facility_Relations(park=park,recreation_facility=rotating_horse,

... joined_date=date(2020,1,27),

... invite_reason='给小孩子玩')

>>> r1.save()

>>>

>>> rotating_horse.park_set.all()

]>

>>> park.park_facility_relations_set.all()

]>

>>> park.relations.all()

]>

使用关系对象的 remove() 方法,删除关联关系:

>>> park2=Park(name='颐和园')

>>> park2.save()

>>> Park_Facility_Relations.objects.create(park=park2,recreation_facility=rotating_horse,

... joined_date=date(2020, 1, 27),

... invite_reason='给小孩子玩'

... )

>>>

>>> park2.relations.all()

]>

>>> park2.relations.all()[0]

>>> park2.relations.remove(rotating_horse)

>>> park2.relations.all()

使用关系对象的 clear() 方法,会删除该对象的所有关联关系:

>>> park2.relations.all()

]>

>>> park2.relations.clear()

>>> park2.relations.all()

以游乐设施作为条件,来找出拥有这些设施的公园有哪些:

>>> Park.objects.filter(relations__name__startswith='转')

, ]>

可以通过点语法查询出关联模型中的字段值:

>>> r2=Park_Facility_Relations.objects.get(park=park2,recreation_facility=rotating_horse)

>>> r2.joined_date

datetime.date(2020, 1, 27)

>>> r2.invite_reason

'给小孩子玩'

还可以以公园与游乐设施为条件,找出具体关系:

>>> r3=rotating_horse.park_facility_relations_set.get(park=park2)

>>> r3.joined_date

datetime.date(2020, 1, 27)

>>> r3.invite_reason

'给小孩子玩'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值