ManyToMany 是一種多對多的關系,在用途和使用方法上和外鍵 ForeignKey 類似,
以下是本篇筆記的目錄:
- ManyToMany 的介紹
- through 引數
- through_fields 引數
- ManyToMany關系資料的增刪改查
- 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介紹
如果想獲取更多相關文章,可掃碼關注閱讀:

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548005.html
標籤:Python
