- 一對(duì)多關(guān)系
Django使用django.db.models.ForeignKey定義多對(duì)一關(guān)系。
ForeignKey需要一個(gè)位置參數(shù):與該模型關(guān)聯(lián)的類
class Info(models.Model):
user = models.ForeignKey(other_model,on_delete=models.SET_NULL)
生活中的多對(duì)一關(guān)系:班主任,班級(jí)關(guān)系。一個(gè)班主任可以帶很多班級(jí),但是每個(gè)班級(jí)只能有一個(gè)班主任
class Headmaster(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Class(models.Model):
class_name = models.CharField(max_length=50)
teacher = models.ForeignKey(Headmaster,null=True,on_delete=models.SET_NULL)
def __str__(self):
return self.class_name
>>> H1 = Headmaster(name='漁夫')
>>> H1.save()
>>> H1
<Headmaster: 漁夫>
>>> H2 = Headmaster(name='農(nóng)夫')
>>> H2.save()
>>> Headmaster.objects.all()
[<Headmaster: 漁夫>, <Headmaster: 農(nóng)夫>]
以上創(chuàng)建了兩條老師數(shù)據(jù)
由于我們?cè)O(shè)置外鍵關(guān)聯(lián)可以為空null=True,所以此時(shí)在班級(jí)表創(chuàng)建時(shí),可以直接保存,不需要提供老師數(shù)據(jù)
>>> C1 = Class(class_name='一班')
>>> C2 = Class(class_name='二班')
#如果外鍵設(shè)置不為空時(shí),保存會(huì)引發(fā)以下錯(cuò)誤
# IntegrityError: NOT NULL constraint failed: bbs_class.teacher_id
>>> C1.teacher = H1
>>> C2.teacher = H2
>>> C1.save()
>>> C2.save()
以上創(chuàng)建了兩條老師數(shù)據(jù)
由于我們?cè)O(shè)置外鍵關(guān)聯(lián)可以為空null=True,所以此時(shí)在班級(jí)表創(chuàng)建時(shí),可以直接保存,不需要提供老師數(shù)據(jù)
>>> C1 = Class(class_name='一班')
>>> C2 = Class(class_name='二班')
#如果外鍵設(shè)置不為空時(shí),保存會(huì)引發(fā)以下錯(cuò)誤
# IntegrityError: NOT NULL constraint failed: bbs_class.teacher_id
>>> C1.teacher = H1
>>> C2.teacher = H2
>>> C1.save()
>>> C2.save()
將老師分配個(gè)班級(jí)之后,由于班級(jí)表關(guān)聯(lián)了老師字段,我們可以通過班級(jí)找到對(duì)應(yīng)老師,雖然老師表中沒有關(guān)聯(lián)班級(jí)字段,但是也可以通過老師找到他所帶的班級(jí),這種查詢方式也叫作關(guān)聯(lián)查詢
通過模型類名稱后追加一個(gè)_set,來實(shí)現(xiàn)反向查詢
>>> H1.class_set.all()
<QuerySet [<Class: 一班>]>
由于我們這是一個(gè)一對(duì)多的關(guān)系,也就說明我們的老師可以對(duì)應(yīng)多個(gè)班級(jí)
我們可以繼續(xù)給H1老師分配新的班級(jí)
>>> C3 = Class(class_name='三班')
>>> C3.teacher = H1
>>> C3.save()
>>> H1.class_set.all()
[<Class: 一班>, <Class: 三班>]
一個(gè)班級(jí)只能對(duì)應(yīng)一個(gè)老師,外鍵是唯一的,那么你在繼續(xù)給C1班級(jí)分配一個(gè)新的老師時(shí),會(huì)覆蓋之前的老師信息,并不會(huì)保存一個(gè)新的老師
>>> H3 = Headmaster(name='伙夫')
>>> H3.save()
>>> C1.teacher
<Headmaster: 漁夫>
>>> C1.teacher=H3
>>> C1.save()
>>> C1.teacher
<Headmaster: 伙夫>
把這個(gè)班級(jí)的老師刪除,由于設(shè)置了外鍵字段可以為null,此時(shí)班級(jí)的老師選項(xiàng)為null
>>> t1 = Headmaster.objects.all().first()
>>> t1
<Headmaster: 漁夫>
>>> c1 = Class.objects.all().first()
>>> c1
<Class: 一班>
>>> c1.teacher
<Headmaster: 伙夫>
>>> t1.delete()
(1, {'modelsapp.Headmaster': 1})
>>> c1 = Class.objects.all().first()
>>> c1
<Class: 一班>
>>> c1.teacher
>>> #這里什么都沒有,因?yàn)榇藭r(shí)C1的老師已經(jīng)是個(gè)None了
注意:
1、要記得刪除之后要重新獲取一次數(shù)據(jù),否則查看到的結(jié)果中還是之前獲取到的有老師的班級(jí)數(shù)據(jù)
2、c1 = Class.objects.all().first()或是c1=Class.objects.filter(id=2).first(),都需要加.first(),否則會(huì)報(bào)錯(cuò)?。?
- 多對(duì)多關(guān)系
多對(duì)多關(guān)系在模型中使用ManyToManyField字段定義,多對(duì)多關(guān)系可以是具有關(guān)聯(lián),也可以是沒有關(guān)聯(lián),所以不需要明確指定on_delete屬性
生活中,多對(duì)多關(guān)系:一個(gè)音樂家可以隸屬于多個(gè)樂隊(duì),一個(gè)樂隊(duì)可以有多個(gè)音樂家
class Artist(models.Model):
artist_name = models.CharField(max_length=50)
def __str__(self):
return self.artist_name
class Band(models.Model):
band_name = models.CharField(max_length=50)
artist = models.ManyToManyField(Artist)
def __str__(self):
return self.band_name
創(chuàng)建音樂家以及樂隊(duì)
from bbs.models import Artist,Band
A1 = Artist.objects.create(artist_name='Jack')
A2 = Artist.objects.create(artist_name='Bob')
B1 = Band.objects.create(band_name='FiveMonthDay')
B2 = Band.objects.create(band_name='SHE')
創(chuàng)建出兩個(gè)樂隊(duì)之后對(duì)其進(jìn)行音樂家的添加,多對(duì)多字段添加時(shí),可以使用add函數(shù)進(jìn)行多值增加,在ManyToManyField所在模型的記錄使用add方法
B1.artist.add(A1,A2)
B2.artist.add(A2)
B1樂隊(duì)含有A1,A2兩名成員,B2樂隊(duì)含有A1成員
B1.artist.all()
[<Artist: Bob>, <Artist: Jack>]
B2.artist.all()
<QuerySet [<Artist: Bob>]>
可以在音樂家表中查找某個(gè)音樂家屬于哪些樂隊(duì)
Band.objects.filter(artist=A1) # 這里使用的是我們模型類來進(jìn)行查找。
[<Band: SHE>, <Band: FiveMonthDay>] # A1樂家屬于,SHE以及FiveMonthDay
Band.objects.filter(artist=A2)
[<Band: SHE>]
也可以查找這音樂家在哪個(gè)樂隊(duì)
A1.band_set.all() # 直接通過具體數(shù)據(jù)對(duì)象進(jìn)行查找
[<Band: SHE>, <Band: FiveMonthDay>]
A2.band_set.all()
[<Band: SHE>]
多對(duì)多關(guān)聯(lián)字段的刪除,要使用remove來進(jìn)行關(guān)系的斷開,而不是直接使用delete,remove只會(huì)斷開數(shù)據(jù)之間的聯(lián)系,但是不會(huì)將數(shù)據(jù)刪除
在B1樂隊(duì)中刪除A1音樂家
B1.artist.remove(A1)
B1.artist.all()
<QuerySet [<Artist: Bob>]>
- 關(guān)聯(lián)表的查詢
如果想要查詢的字段在關(guān)聯(lián)表,則使用表名小寫__字段來進(jìn)行跨表查詢操作
創(chuàng)建一個(gè)多對(duì)一關(guān)系的父子表,一個(gè)父親可能有多個(gè)兒子
class Father(models.Model):
name = models.CharField(max_length=30)
age = models.CharField(max_length=30)
def __str__(self):
return self.name
class Son(models.Model):
father = models.ForeignKey(Father,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
創(chuàng)建父親和兒子們
f1 = Father.objects.create(name='Jack',age='30')
s1 = Son.objects.create(name='Json',father=f1)
s2 = Son.objects.create(name='Json2',father=f1)
f2 = Father.objects.create(name='Bob',age='40')
s3 = Son.objects.create(name='Json3',father=f2)
查詢所有父親名字是jack的孩子
Son.objects.filter(father__name__exact='Jack')
[<Son: Json>, <Son: Json2>]
查詢所有兒子名開頭為J的父親
Father.objects.filter(son__name__startswith='J')
[<Father: Jack>, <Father: Jack>, <Father: Bob>]
獲取到某一個(gè)父親的所有孩子,通過某一條數(shù)據(jù)的小寫表名_set反向查詢
f1.son_set.all()
[<Son: Json>, <Son: Json2>]
- 數(shù)據(jù)的反向查詢
默認(rèn)的,當(dāng)有某一條數(shù)據(jù)獲取到之后,我們可以通過模型類名稱加上一個(gè) _set,來實(shí)現(xiàn)反向查詢
現(xiàn)在設(shè)計(jì)兩個(gè)表為軍隊(duì)和士兵表,并且士兵多對(duì)一關(guān)聯(lián)軍隊(duì)
class Aramy(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Soldier(models.Model):
aramy = models.ForeignKey(Aramy,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
def __str__(self):
return self.name
創(chuàng)建一些數(shù)據(jù)
a1 = Aramy(name='一軍')
a1.save()
s1 = Soldier(name='張三',aramy=a1)
s1.save()
s2 = Soldier(name='李四',aramy=a1)
s2.save()
通過soldier_set我們就可以關(guān)聯(lián)到對(duì)應(yīng)的士兵表,并且對(duì)應(yīng)返回結(jié)果可以執(zhí)行我們常用的filter,exclude等查詢操作
a1.soldier_set.all()
[<Soldier: 張三>, <Soldier: 李四>]
a1.soldier_set.filter(name='張三')
[<Soldier: 張三>]
也可以通過定義關(guān)聯(lián)字段中的related_name值,來實(shí)現(xiàn)自定義的反向查詢名字,注意:related_name的值必須唯一
class Aramy(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return self.name
class Soldier(models.Model):
aramy = models.ForeignKey(Aramy,on_delete=models.CASCADE,related_name='soldier')
name = models.CharField(max_length=30)
def __str__(self):
return self.name
接下來通過某條數(shù)據(jù)反向查詢
a1 = Aramy.objects.all()[0]
s1 = Soldier.objects.get(name='張三')
a1.soldier.all()
[<Soldier: 張三>, <Soldier: 李四>]
注意:related_name一定是一個(gè)唯一的值,否則反向查找時(shí)會(huì)出現(xiàn)二異性錯(cuò)誤,也可以將related_name初始化為+,來取消反向查詢