表與表之間的關系可分為以下三種:
一對一: 一個人對應一個身份證号碼,資料字段設定 unique。
一對多: 一個家庭有多個人,一般通過外鍵來實作。
多對多: 一個學生有多門課程,一個課程有很多學生,一般通過第三個表來實作關聯。

接下來我們來看下多表多執行個體。
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):
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()
說明:
1、EmailField 資料類型是郵箱格式,底層繼承 CharField,進行了封裝,相當于 MySQL 中的 varchar。
2、Django1.1 版本不需要聯級删除:on_delete=models.CASCADE,Django2.2 需要。
3、一般不需要設定聯級更新.
4、外鍵在一對多的多中設定:models.ForeignKey("關聯類名", on_delete=models.CASCADE)。
5、OneToOneField = ForeignKey(...,unique=True)設定一對一。
6、若有模型類存在外鍵,建立資料時,要先建立外鍵關聯的模型類的資料,不然建立包含外鍵的模型類的資料時,外鍵的關聯模型類的資料會找不到。
書籍表 Book:title 、 price 、 pub_date 、 publish(外鍵,多對一) 、 authors(多對多)
出版社表 Publish:name 、 city 、 email
作者表 Author:name 、 age 、 au_detail(一對一)
作者詳情表 AuthorDetail:gender 、 tel 、 addr 、 birthday
以下是表格關聯說明:
我們在 MySQL 中執行以下 SQL 插入操作:
方式一: 傳對象的形式,傳回值的資料類型是對象,書籍對象。
步驟:
a. 擷取出版社對象
b. 給書籍的出版社屬性 pulish 傳出版社對象
def add_book(request):
# 擷取出版社對象
pub_obj = models.Publish.objects.filter(pk=1).first()
# 給書籍的出版社屬性publish傳出版社對象
book = models.Book.objects.create(title="菜鳥教程", price=200, pub_date="2010-10-10", publish=pub_obj)
print(book, type(book))
return HttpResponse(book)
方式二: 傳對象 id 的形式(由于傳過來的資料一般是 id,是以傳對象 id 是常用的)。
一對多中,設定外鍵屬性的類(多的表)中,MySQL 中顯示的字段名是:外鍵屬性名_id。
傳回值的資料類型是對象,書籍對象。
a. 擷取出版社對象的 id
b. 給書籍的關聯出版社字段 pulish_id 傳出版社對象的 id
# 擷取出版社對象的id
pk = pub_obj.pk
# 給書籍的關聯出版社字段 publish_id 傳出版社對象的id
book = models.Book.objects.create(title="沖靈劍法", price=100, pub_date="2004-04-04", publish_id=pk)
方式一: 傳對象形式,無傳回值。
a. 擷取作者對象
b. 擷取書籍對象
c. 給書籍對象的 authors 屬性用 add 方法傳作者對象
# 擷取作者對象
chong = models.Author.objects.filter(name="令狐沖").first()
ying = models.Author.objects.filter(name="任盈盈").first()
# 擷取書籍對象
book = models.Book.objects.filter(title="菜鳥教程").first()
# 給書籍對象的 authors 屬性用 add 方法傳作者對象
book.authors.add(chong, ying)
方式二: 傳對象id形式,無傳回值。
a. 擷取作者對象的 id
c. 給書籍對象的 authors 屬性用 add 方法傳作者對象的 id
# 擷取作者對象的id
pk = chong.pk
book = models.Book.objects.filter(title="沖靈劍法").first()
# 給書籍對象的 authors 屬性用 add 方法傳作者對象的id
book.authors.add(pk)
前提:
多對多(雙向均有關聯管理器)
一對多(隻有多的那個類的對象有關聯管理器,即反向才有)
文法格式:
注意:一對多隻能反向
常用方法:
add():用于多對多,把指定的模型對象添加到關聯對象集(關系表)中。
注意:add() 在一對多(即外鍵)中,隻能傳對象( *QuerySet資料類型),不能傳 id(*[id表])。
*[ ] 的使用:
# 方式一:傳對象
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的作者對象添加到這本書的作者集合中
# 方式二:傳對象 id
book_obj.authors.add(*[1,3]) # 将 id=1 和 id=3 的作者對象添加到這本書的作者集合中
return HttpResponse("ok")
反向:小寫表名_set
ying = models.Author.objects.filter(name="任盈盈").first()
book = models.Book.objects.filter(title="沖靈劍法").first()
ying.book_set.add(book)
create():建立一個新的對象,并同時将它添加到關聯對象集之中。
傳回新建立的對象。
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))
remove():從關聯對象集中移除執行的模型對象。
對于 ForeignKey 對象,這個方法僅在 null=True(可以為空)時存在,無傳回值。
author_obj =models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=11)
author_obj.book_set.remove(book_obj)
clear():從關聯對象集中移除一切對象,删除關聯,不會删除對象。
對于 ForeignKey 對象,這個方法僅在 null=True(可以為空)時存在。
無傳回值。
# 清空獨孤九劍關聯的所有作者
book = models.Book.objects.filter(title="菜鳥教程").first()
book.authors.clear()
基于對象的跨表查詢。
查詢主鍵為 1 的書籍的出版社所在的城市(正向)。
book = models.Book.objects.filter(pk=10).first()
res = book.publish.city
print(res, type(res))
查詢明教出版社出版的書籍名(反向)。
反向:對象.小寫類名_set(pub.book_set) 可以跳轉到關聯的表(書籍表)。
pub.book_set.all():取出書籍表的所有書籍對象,在一個 QuerySet 裡,周遊取出一個個書籍對象。
res = pub.book_set.all()
for i in res:
print(i.title)
查詢令狐沖的電話(正向)
正向:對象.屬性 (author.au_detail) 可以跳轉到關聯的表(作者詳情表)
author = models.Author.objects.filter(name="令狐沖").first()
res = author.au_detail.tel
查詢所有住址在黑木崖的作者的姓名(反向)。
一對一的反向,用 對象.小寫類名 即可,不用加 _set。
反向:對象.小寫類名(addr.author)可以跳轉到關聯的表(作者表)。
addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()
res = addr.author.name
菜鳥教程所有作者的名字以及手機号(正向)。
正向:對象.屬性(book.authors)可以跳轉到關聯的表(作者表)。
作者表裡沒有作者電話,是以再次通過對象.屬性(i.au_detail)跳轉到關聯的表(作者詳情表)。
res = book.authors.all()
print(i.name, i.au_detail.tel)
查詢任我行出過的所有書籍的名字(反向)。
author = models.Author.objects.filter(name="任我行").first()
res = author.book_set.all()
查詢菜鳥出版社出版過的所有書籍的名字與價格。
res = models.Book.objects.filter(publish__name="菜鳥出版社").values_list("title", "price")
反向:通過 小寫類名__跨表的屬性名稱(book__title,book__price) 跨表擷取資料。
res = models.Publish.objects.filter(name="菜鳥出版社").values_list("book__title","book__price")
查詢任我行出過的所有書籍的名字。
正向:通過 屬性名稱__跨表的屬性名稱(authors__name) 跨表擷取資料:
反向:通過 小寫類名__跨表的屬性名稱(book__title) 跨表擷取資料:
查詢任我行的手機号。
正向:通過 屬性名稱__跨表的屬性名稱(au_detail__tel) 跨表擷取資料。
反向:通過 小寫類名__跨表的屬性名稱(author__name) 跨表擷取資料。