表與表之間的關(guān)系可分為以下三種:
一對(duì)一: 一個(gè)人對(duì)應(yīng)一個(gè)身份證號(hào)碼,數(shù)據(jù)字段設(shè)置 unique。
一對(duì)多: 一個(gè)家庭有多個(gè)人,一般通過(guò)外鍵來(lái)實(shí)現(xiàn)。
多對(duì)多: 一個(gè)學(xué)生有多門(mén)課程,一個(gè)課程有很多學(xué)生,一般通過(guò)第三個(gè)表來(lái)實(shí)現(xiàn)關(guān)聯(lián)

創(chuàng)建模型
接下來(lái)我們來(lái)看下多表多實(shí)例。
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
authors = models.ManyToManyField("Author")
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField()
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)
class AuthorDetail(models.Model):
gender_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()
說(shuō)明:
1、EmailField 數(shù)據(jù)類型是郵箱格式,底層繼承 CharField,進(jìn)行了封裝,相當(dāng)于 MySQL 中的 varchar。
2、Django1.1 版本不需要聯(lián)級(jí)刪除:on_delete=models.CASCADE,Django2.2 需要。
3、一般不需要設(shè)置聯(lián)級(jí)更新.
4、外鍵在一對(duì)多的多中設(shè)置:models.ForeignKey("關(guān)聯(lián)類名", on_delete=models.CASCADE)。
5、OneToOneField = ForeignKey(...,unique=True)設(shè)置一對(duì)一。
6、若有模型類存在外鍵,創(chuàng)建數(shù)據(jù)時(shí),要先創(chuàng)建外鍵關(guān)聯(lián)的模型類的數(shù)據(jù),不然創(chuàng)建包含外鍵的模型類的數(shù)據(jù)時(shí),外鍵的關(guān)聯(lián)模型類的數(shù)據(jù)會(huì)找不到。
表結(jié)構(gòu)
- 書(shū)籍表 Book:title 、 price 、 pub_date 、 publish(外鍵,多對(duì)一) 、 authors(多對(duì)多)
- 出版社表 Publish:name 、 city 、 email
- 作者表 Author:name 、 age 、 au_detail(一對(duì)一)
-
作者詳情表 AuthorDetail:gender 、 tel 、 addr 、 birthday
以下是表格關(guān)聯(lián)說(shuō)明:
圖片.png
插入數(shù)據(jù)
我們?cè)?MySQL 中執(zhí)行以下 SQL 插入操作:
insert into app01_publish(name,city,email) values
("華山出版社", "華山", "hs@163.com"), ("明教出版社", "黑木崖", "mj@163.com")
""" 先插入 authordetail 表中多數(shù)據(jù)"""
insert into app01_authordetail(gender,tel,addr,birthday) values
(1,13432335433,"華山","1994-5-23"), (1,13943454554,"黑木崖","1961-8-13"),
(0,13878934322,"黑木崖","1996-5-20")
""" 再將數(shù)據(jù)插入 author,這樣 author 才能找到 authordetail"""
insert into app01_author(name,age,au_detail_id) values
("令狐沖",25,1), ("任我行",58,2), ("任盈盈",23,3)
ORM - 添加數(shù)據(jù)
一對(duì)多(外鍵 ForeignKey)
- 方式一: 傳對(duì)象的形式,返回值的數(shù)據(jù)類型是對(duì)象,書(shū)籍對(duì)象。
步驟:
a. 獲取出版社對(duì)象
b. 給書(shū)籍的出版社屬性 pulish 傳出版社對(duì)象
def add_book(request):
""" 獲取出版社對(duì)象"""
pub_obj = models.Publish.objects.filter(pk=1).first()
""" 給書(shū)籍的出版社屬性publish傳出版社對(duì)象"""
book = models.Book.objects.create(
title="菜鳥(niǎo)教程", price=200, pub_date="2010-10-10", publish=pub_obj)
print(book, type(book))
return HttpResponse(book)
- 方式二: 傳對(duì)象 id 的形式(由于傳過(guò)來(lái)的數(shù)據(jù)一般是 id,所以傳對(duì)象 id 是常用的)。
一對(duì)多中,設(shè)置外鍵屬性的類(多的表)中,MySQL 中顯示的字段名是:外鍵屬性名_id。
返回值的數(shù)據(jù)類型是對(duì)象,書(shū)籍對(duì)象。
步驟:
a. 獲取出版社對(duì)象的 id
b. 給書(shū)籍的關(guān)聯(lián)出版社字段 pulish_id 傳出版社對(duì)象的 id
def add_book(request):
""" 獲取出版社對(duì)象"""
pub_obj = models.Publish.objects.filter(pk=1).first()
""" 獲取出版社對(duì)象的id"""
pk = pub_obj.pk
""" 給書(shū)籍的關(guān)聯(lián)出版社字段 publish_id 傳出版社對(duì)象的id"""
book = models.Book.objects.create(
title="沖靈劍法", price=100, pub_date="2004-04-04", publish_id=pk)
print(book, type(book))
return HttpResponse(book)
多對(duì)多(ManyToManyField):在第三張關(guān)系表中新增數(shù)據(jù)
- 方式一: 傳對(duì)象形式,無(wú)返回值。
步驟:
a. 獲取作者對(duì)象
b. 獲取書(shū)籍對(duì)象
c. 給書(shū)籍對(duì)象的 authors 屬性用 add 方法傳作者對(duì)象
def add_book(request):
""" 獲取作者對(duì)象"""
chong = models.Author.objects.filter(name="令狐沖").first()
ying = models.Author.objects.filter(name="任盈盈").first()
""" 獲取書(shū)籍對(duì)象"""
book = models.Book.objects.filter(title="python入門(mén)").first()
""" 給書(shū)籍對(duì)象的 authors 屬性用 add 方法傳作者對(duì)象"""
book.authors.add(chong, ying)
return HttpResponse(book)
- 方式二: 傳對(duì)象id形式,無(wú)返回值。
步驟:
a. 獲取作者對(duì)象的 id
b. 獲取書(shū)籍對(duì)象
c. 給書(shū)籍對(duì)象的 authors 屬性用 add 方法傳作者對(duì)象的 id
def add_book(request):
""" 獲取作者對(duì)象"""
chong = models.Author.objects.filter(name="令狐沖").first()
""" 獲取作者對(duì)象的id"""
pk = chong.pk
""" 獲取書(shū)籍對(duì)象"""
book = models.Book.objects.filter(title="沖靈劍法").first()
""" 給書(shū)籍對(duì)象的 authors 屬性用 add 方法傳作者對(duì)象的id"""
book.authors.add(pk)
關(guān)聯(lián)管理器(對(duì)象調(diào)用)
前提:
- 多對(duì)多(雙向均有關(guān)聯(lián)管理器)
- 一對(duì)多(只有多的那個(gè)類的對(duì)象有關(guān)聯(lián)管理器,即反向才有)
語(yǔ)法格式:
正向:屬性名
反向:小寫(xiě)類名加 _set
注意:一對(duì)多只能反向
常用方法:
1、add():用于多對(duì)多,把指定的模型對(duì)象添加到關(guān)聯(lián)對(duì)象集(關(guān)系表)中。
注意:add() 在一對(duì)多(即外鍵)中,只能傳對(duì)象( QuerySet數(shù)據(jù)類型),不能傳 id([id表])。
*[ ] 的使用:
# 方式一:傳對(duì)象
book_obj = models.Book.objects.get(id=10)
author_list = models.Author.objects.filter(id__gt=2)
book_obj.authors.add(*author_list) # 將 id 大于2的作者對(duì)象添加到這本書(shū)的作者集合中
# 方式二:傳對(duì)象 id
book_obj.authors.add(*[1,3]) # 將 id=1 和 id=3 的作者對(duì)象添加到這本書(shū)的作者集合中
return HttpResponse("ok")
反向:小寫(xiě)表名_set
ying = models.Author.objects.filter(name="任盈盈").first()
book = models.Book.objects.filter(title="沖靈劍法").first()
ying.book_set.add(book)
return HttpResponse("ok")
2、create():創(chuàng)建一個(gè)新的對(duì)象,并同時(shí)將它添加到關(guān)聯(lián)對(duì)象集之中。
返回新創(chuàng)建的對(duì)象。
pub = models.Publish.objects.filter(name="明教出版社").first()
wo = models.Author.objects.filter(name="任我行").first()
book = wo.book_set.create(title="吸星大法", price=300, pub_date="1999-9-19", publish=pub)
print(book, type(book))
return HttpResponse("ok")
3、remove():從關(guān)聯(lián)對(duì)象集中移除執(zhí)行的模型對(duì)象。
對(duì)于 ForeignKey 對(duì)象,這個(gè)方法僅在 null=True(可以為空)時(shí)存在,無(wú)返回值。
author_obj =models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
return HttpResponse("ok")
4、clear():從關(guān)聯(lián)對(duì)象集中移除一切對(duì)象,刪除關(guān)聯(lián),不會(huì)刪除對(duì)象。
對(duì)于 ForeignKey 對(duì)象,這個(gè)方法僅在 null=True(可以為空)時(shí)存在。
無(wú)返回值。
""" 清空獨(dú)孤九劍關(guān)聯(lián)的所有作者"""
book = models.Book.objects.filter(title="獨(dú)孤九劍").first()
book.authors.clear()
ORM 查詢
一、基于對(duì)象的跨表查詢。
正向:屬性名稱
反向:小寫(xiě)類名_set
1、一對(duì)多
查詢主鍵為 1 的書(shū)籍的出版社所在的城市。
book = models.Book.objects.filter(pk=1).first()
res = book.publish.city
print(res, type(res))
return HttpResponse("ok")
查詢明教出版社出版的書(shū)籍名。
反向:對(duì)象.小寫(xiě)類名_set(pub.book_set) 可以跳轉(zhuǎn)到關(guān)聯(lián)的表(書(shū)籍表)。
pub.book_set.all():取出書(shū)籍表的所有書(shū)籍對(duì)象,在一個(gè) QuerySet 里,遍歷取出一個(gè)個(gè)書(shū)籍對(duì)象。
pub = models.Publish.objects.filter(name="明教出版社").first()
res = pub.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")
2、一對(duì)一
查詢令狐沖的電話。
正向:對(duì)象.屬性 (author.au_detail) 可以跳轉(zhuǎn)到關(guān)聯(lián)的表(作者詳情表)
author = models.Author.objects.filter(name="令狐沖").first()
res = author.au_detail.tel
print(res, type(res))
return HttpResponse("ok")
查詢所有住址在黑木崖的作者的姓名。
一對(duì)一的反向,用 對(duì)象.小寫(xiě)類名 即可,不用加 _set。
反向:對(duì)象.小寫(xiě)類名(addr.author)可以跳轉(zhuǎn)到關(guān)聯(lián)的表(作者表)。
addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()
res = addr.author.name
print(res, type(res))
return HttpResponse("ok")
3、多對(duì)多
Django教程所有作者的名字以及手機(jī)號(hào)。
正向:對(duì)象.屬性(book.authors)可以跳轉(zhuǎn)到關(guān)聯(lián)的表(作者表)。
作者表里沒(méi)有作者電話,因此再次通過(guò)對(duì)象.屬性(i.au_detail)跳轉(zhuǎn)到關(guān)聯(lián)的表(作者詳情表)。
book = models.Book.objects.filter(title="Django教程").first()
res = book.authors.all()
for i in res:
print(i.name, i.au_detail.tel)
return HttpResponse("ok")
查詢?nèi)挝倚谐鲞^(guò)的所有書(shū)籍的名字。
author = models.Author.objects.filter(name="任我行").first()
res = author.book_set.all()
for i in res:
print(i.title)
return HttpResponse("ok")
二、基于雙下劃線的跨表查詢
正向:屬性名稱__跨表的屬性名稱
反向:小寫(xiě)類名__跨表的屬性名稱
1、一對(duì)多
查詢江湖出版社出版過(guò)的所有書(shū)籍的名字與價(jià)格。
res = models.Book.objects.filter(
publish__name="江湖出版社").values_list("title", "price")
通過(guò) 小寫(xiě)類名__跨表的屬性名稱(book__title,book__price) 跨表獲取數(shù)據(jù)。
res = models.Publish.objects.filter(name="江湖出版社").values_list(
"book__title","book__price")
return HttpResponse("ok")
注:
values()得到的是一個(gè)字典形式的查詢集(QuerySet),查詢集是一個(gè)可迭代對(duì)象。
values_list返回的是元組列表,values_list加上flat=True之后返回值列表。
2、多對(duì)多
查詢?nèi)挝倚谐鲞^(guò)的所有書(shū)籍的名字。
通過(guò) 屬性名稱__跨表的屬性名稱(authors__name) 跨表獲取數(shù)據(jù):
res = models.Book.objects.filter(authors__name="任我行").values_list("title")
通過(guò) 小寫(xiě)類名__跨表的屬性名稱(book__title) 跨表獲取數(shù)據(jù):
res = models.Author.objects.filter(name="任我行").values_list("book__title")
3、一對(duì)一
查詢?nèi)挝倚械氖謾C(jī)號(hào)。
通過(guò) 屬性名稱__跨表的屬性名稱(au_detail__tel) 跨表獲取數(shù)據(jù)
res = models.Author.objects.filter(name="任我行").values_list("au_detail__tel")
通過(guò) 小寫(xiě)類名__跨表的屬性名稱(author__name) 跨表獲取數(shù)據(jù)。
res = models.AuthorDetail.objects.filter(author__name="任我行").values_list("tel")
