Django--ORM多表操作
阿新 • • 發佈:2018-11-14
一 建立表模型
假設:
作者模型:一個作者有姓名和年齡。
作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等資訊。作者詳情模型和作者模型之間是一對一的關係(one-to-one)
出版商模型:出版商有名稱,所在城市以及email。
書籍模型: 書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關係就是多對多的關聯關係(many-to- many);一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關係(one-to-many)。
from django.db import models # Create your models here. class Publish(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) addr = models.CharField(max_length=32) email = models.EmailField() class Author(models.Model): id = models.AutoField(primary_key=True) name= models.CharField(max_length=32) sex = models.IntegerField() authordetail = models.OneToOneField(to='AuthorDetail', to_field='id') class AuthorDetail(models.Model): id = models.AutoField(primary_key=True) phone = models.CharField(max_length=32) addr = models.CharField(max_length=64)class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=5, decimal_places=2) publish = models.ForeignKey(to=Publish, to_field='id') authors = models.ManyToManyField(to=Author) def __str__(self): return self.name
二 增刪改查
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day77.settings") import django django.setup() from app01.models import * # 一對多新增資料 # 新增一本北京出版社出版的書 # 第一種方式 # ret=Book.objects.create(name='紅樓夢',price=34.5,publish_id=1) # print(ret.name)
# 第二種方式,存物件publish=出版社的物件,存到資料庫,是一個id # publish=Publish.objects.get(id=1) # pk是主鍵,通過主鍵查詢 # publish=Publish.objects.get(pk=1) # publish = Publish.objects.filter(pk=2).first() # ret = Book.objects.create(name='西遊記', price=34.5, publish=publish) # print(ret.name)
# 一對多修改資料 # book=Book.objects.get(pk=1) # # book.publish=出版社物件 # book.publish_id=2 # book.save()
# 方式二 # book=Book.objects.filter(pk=1).update(publish=出版社物件) # book=Book.objects.filter(pk=1).update(publish_id=1)
# 多對多新增 # 為紅樓夢這本書新增一個叫lqz,egon的作者 # lqz=Author.objects.filter(name='lqz').first() # egon=Author.objects.filter(name='egon').first() # book=Book.objects.filter(name='紅樓夢').first() # # add 新增多個物件 # book.authors.add(lqz,egon) # add新增作者id # book.authors.add(1,2)
#刪除 remove,可以傳物件,可以傳id,可以傳多個,不要混著用 # book.authors.remove(lqz) # book.authors.remove(2) # book.authors.remove(1,2)
# clear清空所有 # book.authors.clear() # set,先清空,在新增,要傳一個列表,列表內可以是, id,也可以是物件 # book.authors.set([lqz,]) # ********這樣不行,因為它打散了傳過去了,相當於book.authors.set(lqz) # book.authors.set(*[lqz,])
三 基於物件的跨表查詢
''' 一對一 正向 author---關聯欄位在author--->authordetail ------> 按欄位 反向 authordetail------關聯欄位在author--->author -----> 按表名小寫 ''' # 查詢lqz作者的手機號 正向查詢 # author=Author.objects.filter(name='lqz').first() # # author.authordetail 就是作者詳情的物件 # authordetail=author.authordetail # print(authordetail.phone) # 查詢地址是 :山東 的作者名字 反向查詢 # authordetail=AuthorDetail.objects.filter(addr='山東').first() # # authordetail.author 這是作者物件 # author=authordetail.author # print(author.name) #一對多 ''' 一對多 正向 book---關聯欄位在book--->publish ------> 按欄位 反向 publish------關聯欄位在book--->book -----> 按表名小寫_set.all() ''' # 正向 查詢紅樓夢這本書的出版社郵箱 # book=Book.objects.filter(name='紅樓夢').first() # # book.publish 就是出版社物件 # pulish=book.publish # print(pulish.email) # 反向 查詢地址是北京 的出版社出版的圖書 # publish=Publish.objects.filter(addr='北京').first() # # publish.book_set.all() 拿出所有的圖書 # books=publish.book_set.all() # # 統計一下條數 # books=publish.book_set.all().count() # print(books) ''' 多對多 正向 book---關聯欄位在book--->author ------> 按欄位.all() 反向 author------關聯欄位在book--->book -----> 按表名小寫_set.all() ''' #查詢紅樓夢這本書所有的作者 # book=Book.objects.filter(name='紅樓夢').first() # book.authors.all() #是所有的作者,是一個queryset物件,可以繼續點 # print(book.authors.all()) # 查詢lqz寫的所有書 # lqz=Author.objects.filter(name='lqz').first() # books=lqz.book_set.all() # print(books) # 連續跨表 # 查詢紅樓夢這本書所有的作者的手機號 # book=Book.objects.filter(name='紅樓夢').first() # authors=book.authors.all() # for author in authors: # authordetail=author.authordetail # print(authordetail.phone)
基於物件的查詢---是子查詢也就是多次查詢
四 基於雙下劃線的查詢
# 一對一 # 查詢lqz作者的手機號 正向查詢 跨表的話,按欄位 # 以author表作為基表 # ret=Author.objects.filter(name='lqz').values('authordetail__phone') # print(ret)
# 以authordetail作為基表 反向查詢,按表名小寫 跨表的話,用表名小寫 # ret=AuthorDetail.objects.filter(author__name='lqz').values('phone') # print(ret)
# 查詢lqz這個作者的性別和手機號 # 正向 # ret=Author.objects.filter(name='lqz').values('sex','authordetail__phone') # print(ret) # 查詢手機號是13888888的作者性別 # ret=Author.objects.filter(authordetail__phone='13888888').values('sex') # print(ret) # ret=AuthorDetail.objects.filter(phone='13888888').values('author__sex') # print(ret) # 基於雙 下劃線的一對多查詢 # 查詢出版社為北京出版社出版的所有圖書的名字,價格 # ret=Publish.objects.filter(name='北京出版社').values('book__name','book__price') # print(ret) # ret=Book.objects.filter(publish__name='北京出版社').values('name','price') # print(ret) # 查詢北京出版社出版的價格大於19的書 # ret=Publish.objects.filter(name='北京出版社',book__price__gt=19).values('book__name','book__price') # print(ret)
# 多對多 # 查詢紅樓夢的所有作者名字 # ret=Book.objects.filter(name='紅樓夢').values('authors__name') # print(ret) # ret=Author.objects.filter(book__name='紅樓夢').values('name') # print(ret) # 查詢圖書價格大於30的所有作者名字 # ret=Book.objects.filter(price__gt=30).values('authors__name') # print(ret)
# 進階練習--連續跨表 # 查詢北京出版社出版過的所有書籍的名字以及作者的姓名 # ret=Publish.objects.filter(name='北京出版社').values('book__name','book__authors__name') # print(ret) # ret=Book.objects.filter(publish__name='北京出版社').values('name','authors__name') # print(ret) # 手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱 # ret=AuthorDetail.objects.filter(phone__startswith='13').values('author__book__name','author__book__publish__name') # print(ret) # ret=Book.objects.filter(authors__authordetail__phone__startswith='13').values('name','publish__name') # print(ret)
五 聚合查詢和分組查詢
from django.db.models import Avg,Count,Max,Min,Sum
#聚合查詢aggregate() # 計算所有圖書的平均價格 # ret=Book.objects.all().aggregate(Avg('price')) # print(ret)
# 計算圖書的最高價格 # ret=Book.objects.all().aggregate(Max('price')) # print(ret)
#aggregate是queryset的終止子句
# 計算圖書的最高價格,最低價格,平均價格,總價 # ret=Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price')) # print(ret) #分組查詢annotate() # 統計每一本書作者個數 # ret=Book.objects.all().annotate(c=Count('authors')) # print(ret) # for r in ret: # print(r.name,'---->',r.c) # ret=Book.objects.all().annotate(c=Count('authors')).values('name','c') # print(ret)
# 統計每一個出版社的最便宜的書(以誰group by 就以誰為基表) # ret=Publish.objects.all().annotate(m=Min('book__price')).values('name','m') # print(ret)
# 統計每一本以py開頭的書籍的作者個數 # ret1=Book.objects.all().filter(name__startswith='py').annotate(c=Count('authors')).values('name','c') # print(ret1)
# 總結: group by 誰,就以誰做基表,filter過濾,annotate取分組,values取值
# 總結終極版本 # values在前,表示group by 在後,表示取值 # filter在前,表示where條件,在後表示having
# 統計每一本以py開頭的書籍的作者個數--套用模板 # ret2=Book.objects.all().values('name').filter(name__startswith='py').annotate(c=Count('authors')).values('name','c') # print(ret2)
# 查詢各個作者出的書的總價格 # ret=Author.objects.all().values('name').annotate(s=Sum('book__price')).values('name','s') # ret=Author.objects.all().annotate(s=Sum('book__price')).values('name','s') # print(ret)
#查詢名字叫lqz作者書的總價格 # ret=Author.objects.all().values('pk').filter(name='lqz').annotate(s=Sum('book__price')).values('name','s') # print(ret)
# 查詢所有作者寫的書的總價格大於30 # ret=Author.objects.all().values('pk').annotate(s=Sum('book__price')).filter(s__gt=2).values('name','s') # ret=Author.objects.all().annotate(s=Sum('book__price')).filter(s__gt=30).values('name','s') # print(ret)
# 總結終極版本 # values在前,表示group by 在後,表示取值 # filter在前,表示where條件,在後表示having
# 統計不止一個作者的圖書 ret=Book.objects.all().values('pk').annotate(c=Count('authors')).filter(c__gt=1).values('name','c') # ret = Book.objects.annotate(author_num=Count("authors")).filter(author_num__gt=1).values('name','author_num') print(ret)
六 F查詢和Q查詢
# F函式
from django.db.models import F # 查詢評論數大於閱讀數的書 # 這樣不行,進行不下去了 # ret=Book.objects.all().filter(commit_num__gt=reat_num) # 這麼來做 # ret=Book.objects.all().filter(commit_num__gt=F('reat_num')) # print(ret) # 把所有書的評論數加1 # 不行,進行不下去 # ret=Book.objects.all().update(commit_num+=1) # 不行,進行不下去 # ret=Book.objects.all().update(commit_num=commit_num+1) # 這樣來做 # ret=Book.objects.all().update(commit_num=F('commit_num')+1) # print(ret) # 把python這本書的閱讀數減5 # ret = Book.objects.all().filter(name='python').update(reat_num=F('reat_num') - 5) # print(ret) # Q函式 為了表示與& ,或 | ,非 ~, from django.db.models import Q
# 查詢作者名字是lqz或者名字是egon的書 # 這樣行不通 # ret=Book.objects.all().filter(authors__name='lqz',authors__name='egon') # 這樣出來 # ret=Book.objects.all().filter(Q(authors__name='lqz')|Q(authors__name='egon')) # print(ret) # 沒有意義 # ret = Book.objects.all().filter(Q(authors__name='lqz') & Q(authors__name='egon')) # print(ret)
# 查詢作者不是lqz的書 # ret=Book.objects.filter(~Q(authors__name='lqz')) # print(ret) # 構建很複雜的邏輯,需要用括號來區分 # ret = Book.objects.filter((Q(name='紅樓夢') & Q(price__gt=100)) | Q(pk__gt=2)) # print(ret)