多表操作是常用的資料庫操作,如果不使用多表操作,建立資料庫是沒有意義的。
多表操作包含三種類型:
- 一對多
- 多對多
- 一對一
每一種類型又包含正向查詢及反向查詢兩個方向。
為了更好的練習多表操作,我們要先創建資料庫表的模型:
1 #書籍表,與作者建立多對多關係,出版社建立多對一關係 2 class Book(models.Model): 3 nid = models.AutoField(primary_key=True) 4 title = models.CharField(max_length=32) 5 price = models.DecimalField(max_digits=8,decimal_places=2) 6 pub_date = models.DateTimeField() 7 publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE) #級聯刪除 8 authors = models.ManyToManyField(to="Author") 9 def __str__(self): 10 return self.title 11 12 #出版社表,與書籍建立一對多關係 13 class Publish(models.Model): 14 nid = models.AutoField(primary_key=True) 15 name = models.CharField(max_length=32) 16 email = models.CharField(max_length=32) 17 18 #作者表,與書籍建立多對多關係,與作者詳情建立一對一關係 19 class Author(models.Model): 20 nid = models.AutoField(primary_key=True) 21 name = models.CharField(max_length=32) 22 age = models.IntegerField() 23 email = models.CharField(max_length=32) 24 ad = models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE,null=True) 25 # ad = models.ForeignKey("AuthorDetail",on_delete=models.CASCADE,unique=True) 26 def __str__(self): 27 return self.name 28 29 #作者詳情 30 class AuthorDetail(models.Model): 31 addr = models.CharField(max_length=32) 32 tel=models.IntegerField()
書籍表 | 出版社表 | 作者表 | 作者詳情 | |
書籍表 | 多對一 | 多對多 | ||
出版社表 | 一對多 | |||
作者表 | 多對多 | 一對一 | ||
作者詳情 | 一對一 |
使用命令產生資料庫表:
python manage.py makemigrations
python manage.py migrate
資料庫表格如下:
- bookstore_author
- bookstore_authordetail
- bookstore_book
- bookstore_book_authors
- bookstore_publish
與建立單表相同,bookstore為創建的app名稱,下劃線後為表格名稱,django會自動將所有大寫字母轉為小寫字母。
與建立單表不同的:
- 在bookstore_book中,有一個字段publish使用 Foreignkey 關聯了出版社的nid,會將表格欄位名稱設為 'fieldname' + '_id'
- bookstore_book_authors為django為多對多關係所自動建立的關係表,表示在book表中與author所建立的關係。
表格建立完成後,我們再新增一些數據:
#使用SQL語法新增數據 #新增出版社 insert into bookstore_publish (name,email) values('蘋果出版社',122); insert into bookstore_publish (name,email) values('橘子出版社',123);
新增書籍對出版社一對多關聯
#In views.py
#使用django models物件方法來新增
from bookstore import models def addData(request): #建立一對多關聯的方式有兩種 #方法一,直接以id新增: book = models.Book.objects.create( title='python', price=123, pub_date='2019-11-11', publish_id = 1, #使用id新增 ) #方法二,以物件新增: publish_obj = models.Publish.objects.get(name='橘子出版社') #必須先產生出版社物件 #publish_obj = models.Publish.objects.filter(name='橘子出版社').first() book = models.Book.objects.create( title='linux', price=150, pub_date='2019-12-12', publish = publish_ob, #使用物件新增 )
在我們新增書籍對作者的關聯之前,我們的需要先新增一些作者
INSERT INTO bookstore_author (name,age,email) VALUES('Alex',11,111); INSERT INTO bookstore_author (name,age,email) VALUES('Egon',21,222); INSERT INTO bookstore_author (name,age,email) VALUES('Jacky',13,333);
再來我們建立一些作者詳情
INSERT INTO bookstore_authordetail (addr,tel) VALUES('台北',111); INSERT INTO bookstore_authordetail (addr,tel) VALUES('台中',222); INSERT INTO bookstore_authordetail (addr,tel) VALUES('高雄',333);
作者與作者詳情是有一對一關係的,所以我們讓他們建立起來。一對一也就是 Foreignkey 加上 unique 。
#搜尋相關作者詳情紀錄的物件 alexad = models.AuthorDetail.objects.(tel=111) egonad = models.AuthorDetail.objects.(tel=222) jackyad = models.AuthorDetail.objects.(tel=333) #找出作者的QuerySet alex = models.Auhtor.objects.filter(name='Alex') egon = models.Auhtor.objects.filter(name='Egon') jacky = models.Auhtor.objects.filter(name='Jacky') #更新作者中的作者詳情 alex.update(ad=alexad) egon.update(ad=egonad) jacky.update(ad=jackyad)
有了作者以後,我們就可以開始增加書籍與作者的關係了。
書籍對作者多對多關聯操作,增刪改
#先找出書籍物件 book = models.Book.objects.filter(title='python').first() jacky = models.Author.objects.filter(name='Jacky').first() #新增書籍對作者的多對多關聯 #新增一個 book.authors.add(4) #或 book.authors.add(jacky)
#新增多個
book.authors.add(*[1,2,3])
###移除多對多關連###
#移除Python這本書對所有authors_id==4的作者
book.authors.remove(4)
#移除Python這本書所有的多對多關連
book.authors.clear()
#更新
book.authors.set([1,2,3])
#等價於
book.authors.clear()
book.authors.add(1,2,3)
表格建立完成以後,我們就可以做一系列的查詢練習了。
基於對象的跨表查詢
一對多
'''
正向查詢 : 關聯屬性所在的表查詢關連表紀錄
反向查詢 :
正向查詢:按字段:book.publish
Book -----------------------------------------------> Publish
<----------------------------------------------
反向查詢表名小寫_set.all():pub_obj.book_set.all()
'''
1. 查詢python 這本書出版社的名字和郵箱
book = models.Book.objects.filter(title="python").first() query = book.publishprint(query.name,query.email)
多對多
正向查詢按字段 book.author.all()
Book ------------------------------------------------> Author
<-----------------------------------------------
反向查詢表名小寫_set.all():alex.book_set.all()
2 蘋果出版社出版的所有書籍的名稱
publish = models.Publish.objects.filter(name="蘋果出版社").first() query = publish.book_set.all() print(query)
#3 查詢python書籍作者的年齡
# book = models.Book.objects.filter(title="python").first()
# author = book.authors.all().values("age") #與這本書關聯的作者年齡
# print(author)
#######一對一############
'''
正向查詢按字段 alex.ad
Author ------------------------------------------------> AuthorDetail
<-------------------------------------------------
反向查詢表名小寫ad.author:ad.author
'''
基於雙下劃線的跨表查詢(基於join實現)
#1 查詢python 這本書出版社的名字和郵箱 ret = models.Book.objects.filter(title='python').values('publish__name') ret = models.Publish.objects.filter(book__title="python").values('name','email') #2 蘋果出版社出版的所有書籍的名稱 ret = models.Publish.objects.filter(name='蘋果出版社').values_list('book__title')
ret = models.Book.objects.filter(publish__name="蘋果出版社").values('title')
#3 查詢python書籍作者的年齡
ret = models.Book.objects.filter(title='python').values('authors__age')
#4 查詢alex出版過的所有書籍名稱
models.Author.objects.filter(name='alex').values('book__title')
models.Book.objects.filter(authors__name='Alex').values('title')
#5 查詢alex的手機號
models.Author.objects.filter(name='Alex').values('authordetail__tel')
models.AuthorDetail.objects.filter(author__name='Alex').values('tel')
#6 查詢手機號為110的作者的名稱
models.Author.objects.filter(authordetail__tel=110).values('name')
models.AuthorDetail.objects.filter(tel=110).values('author_name')
#########連續跨表########
# 查詢人民出版社出版過的所有書籍的名字以及作者的姓名 publish.objects.filter(name="蘋果出版社").values("book__title","book__authors__name") #手機號以110開頭的作者出版過的所有書籍名稱以及出版社名稱 ret = models.AuthorDetail.objects.filter(tel=110).values('author__book__title','author__book__publish__name')
#########聚合分組查詢######
#查詢所有書籍的平均價格 from django.db.models import Avg,Max,Sum,Min,Count models.Book.objects.all().aggregate(priceAvg=Avg("price")) print(ret) #{"price_avg":123} #查詢所有書籍的個數 models.objects.all().aggregate(c=Count("nid")) #分組 #單表分組查詢 #查詢書籍表每一個出版社id以及對應的書籍數 select publish_id,Count(1) from book group by publish_id; #key:annotate()前values哪一個字段就按哪一個字段group by models.Book.objects.values("publish_id").annotate(count=Count(1))
#跨表分組查詢
# 查詢每一個出版社的名稱以及對應的書籍平均價格 ret = models.Publish.objects.values("name","email").annotate(book_count=Avg('book__price')) # 查詢每一個出版社的名稱以及對應的書籍數 ret = models.Publish.objects.values("name").annotate(book_count=Count('book')) # 查詢每一個作者的名字以及出版的書籍的最高價格 ret = models.Author.objects.values('name').annotate(max_price=Max('book__price')) # 统计不止一个作者的图书: ret = models.Book.objects.annotate(c=Count('authors')).filter(c__gt=1) select `bookstore_book`.`title`,Count(`bookstore_book_authors`.`author_id`) as author_count from bookstore_book inner join bookstore_book_authors on `bookstore_book`.`nid` = `bookstore_book_authors`.`book_id` group by `bookstore_book`.`nid` having author_count >=1