一對(duì)多及多對(duì)多關(guān)系

  • 一對(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初始化為+,來取消反向查詢

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容