首先了解一下 mysql中的表之間的關系,一對一,一對多,多對一,多對多。
一對多關系、多對一關系、一對一關系
- 至少都有一側是單個實體,所以記錄之間的聯系通過外鍵實現,讓外鍵指向這個實體。
- 實現這種關系時,要在“多”這一側加入一個外鍵,指向“一”這一側聯接的記錄。
多對多關系
- 解決方法是添加第三個表,這個表稱為關聯表。
- 多對多關系可以分解成原表和關聯表之間的兩個一對多關系
多對多關系例子
查詢多對多關系要分成兩步。
- 若想知道某位學生選擇了哪些課程,要先從學生和注冊之間的一對多關系開始, 獲取這位學生在 registrations 表中的所有記錄。
- 然后再按照多到一的方向遍歷課程和注冊之間的一對多關系, 找到這位學生在 registrations 表中各記錄所對應的課程。
- 同樣,若想找到選擇了某門課程的所有學生,你要先從課程表中開始,獲取其在 registrations 表中的記錄,再獲取這些記錄聯接的學生。
自引用關系也是多對多的一種特殊情況
如果關系中的兩側都在同一個表中, 這種關系稱為自引用關系。在關注中, 關系的左側是用戶實體,可以稱為“關注者”;關系的右側也是用戶實體,但這些是“被關注者”。
創建一個多表之間關系的例子
實例:我們來假定下面這些概念,字段和關系
作者模型:一個作者有姓名和年齡。
作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。作者詳情模型和作者模型之間是一對一的關系(one-to-one)
出版商模型:出版商有名稱,所在城市以及email。
書籍模型: 書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關系就是多對多的關聯關系(many-to-many);一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關系(one-to-many)。
模型建立如下:
from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) age = models.IntegerField() # 與AuthorDetail建立一對一的關系 authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE) class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday = models.DateField() telephone = models.BigIntegerField() addr = models.CharField(max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=32) publishDate = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2) # 與Publish建立一對多的關系,外鍵字段建立在多的一方 publish = models.ForeignKey(to="Publish", to_field="nid", on_delete=models.CASCADE) # 與Author表建立多對多的關系,ManyToManyField可以建在兩個模型中的任意一個,自動創建第三張表 authors = models.ManyToManyField(to='Author', )
添加記錄
#方式一:如果是這樣直接指定publish_id字段去添加值,前提是你的主表里面必須有數據 # 主表:沒有被關聯的(因為book表是要依賴於publish這個表的)也就是publish表 # 子表:關聯的表 book_obj = Book.objects.create(title='小豬豬', publishDate='2012-12-12', price=200, publish_id=1) #方式二 建議 pub_obj = Publish.objects.filter(name='西瓜出版社')[0]) Book.objects.create(title='神雕俠侶',publishDate='2013-12-12',price=188,publish=pub_obj)
#第二種方法可以避免出版社不存在的問題
多對多添加記錄:
書和作者是多對多的關系:一個書可以有多個作者,一個作者可以出版多本書
步驟:先找到書對象
再找到需要的作者對象
給書對象綁定作者對象(用add方法),也就是綁定多對多的關系.
#方式一
#查找作者id xiaojiu =Author.objects.filter(name='xiaojiu').first() zhang = Author.objects.filter(name='zhang').first() #給書籍綁定作者 book_obj.authors.add(zhang,xiaojiu)
#方式二
pub_obj = Publish.objects.filter(name='蘋果出版社').first()
book_obj = Book.objects.create(title='蘋果書',publishDate='2012-2-2',price=188,publish=pub_obj)
authers = Author.objects.all()
綁定多對多的關系
book_obj.authors.add(*authers)
解除綁定:remove
# 將某個特定的對象從被關聯對象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj = Book.objects.filter(title='小豬豬5').last() #找到對象 authers = Author.objects.filter(nid__lt=3)#找到符合作者的對象 book_obj.authors.remove(*authers) #因為是多個所以要加*
清除綁定:clear”
#清空被關聯對象集合。
book_obj = Book.objects.filter(title='小豬豬4')#找到對象 for book_obj_item in book_obj: #循環賦值 book_obj_item.authors.clear()#清空關聯對象
總結:remove和clear的區別
remove:得吧你要清除的數據篩選出來,然后移除
clear:不用查,直接就把數據都清空了。
各有應用場景
一對一查詢
author和authordetile是一對一的關系
正向查詢(按字段author)
反向查詢(按表名authordeital):因為是一對一的關系了,就不用_set了。
#一對一查詢 #正向查詢: 手機號為110的作者姓名 deital_obj = AuthorDetail.objects.filter(telephone=110).first() print(deital_obj.author.name) #反向查詢:查詢作者的手機號 xiaojiu_obj = Author.objects.filter(name='xiaojiu').first() print(dir(xiaojiu_obj)) #如果找不到方法,用dir 看看這個對象都有什么方法 print(xiaojiu_obj.authorDetail.telephone)
一對多查詢
正向查詢(按字段:publish):
反向查詢(按表名:book_set):
#正向查詢:查詢 小紅帽這本書的出版社地址 book_obj = Book.objects.filter(title='小紅帽')[0] #拿到書對象 print('=======',book_obj.publish) print(book_obj.publish.city) #反向查詢:查詢蘋果出版社都初版過哪本書和價格 pub_obj = Publish.objects.filter(name='蘋果出版社')[0] book_dic = pub_obj.book_set.all().values('price','title')[0] print(book_dic) print(book_dic['price']) #查詢出版過的所有書籍 book_list = pub_obj.book_set.all() for book_obj in book_list: print(book_obj.title,book_obj.price)
庄傑大佬解答了我的疑惑, 在這里謝謝大佬,小弟甘拜下風
多對多查詢
正向查詢(按字段authorlist)
反向查詢(按表名book_set)
#多對多查詢 #正向查詢:查詢小紅帽這本書的所有作者的姓名和年齡 book_obj = Book.objects.filter(title='小豬豬3')[0] print(book_obj.authors.all().values('name','age')) #反向查詢 : 查詢作者是xiaojiu的 出了那幾本書 #方法一: 使用book_set.all xiaojiu_obj = Author.objects.filter(name='zhang').first() alls= (xiaojiu_obj.book_set.all()) for i in alls: print(i.title) #方法二:使用values author_list = xiaojiu_obj.book_set.all().values('title') for i in author_list: print(i.get('title'))
你可以通過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下來就會如我們看到這般
# 查詢 人民出版社出版過的所有書籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 與人民出版社關聯的所有書籍對象集合
基於雙下划線的查詢
Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關系,它能自動確認 SQL JOIN 聯系。要做跨關系查詢,就使用兩個下划線來鏈接模型(model)間關聯字段的名稱,直到最終鏈接到你想要的 model 為止。(相當於用sql語句用join連接的方式,可以在settings里面設置,可查看sql語句)
一對多查詢
#練習1、蘋果出版社出版過的所有的書的價格和名字
#基於雙下划線的查詢方式 #第一種查法 ret = Publish.objects.filter(name='蘋果出版社').values('book__title','book__price') # #先找到蘋果出版社這個對象,之后再通過vlaues取值,book表下的什么字段中間用__鏈接,固定寫法 print(ret) #第二種查法 #對比第一種方法,這種方法就是先join publish表,之后再進行取值。 ret2 = Book.objects.filter(publish__name='蘋果出版社').values('title','price') print(ret2)
#練習2、查詢手機號以11開頭的作者出版過的所有書的名稱以及出版社的名稱
#方式1 author_obj = AuthorDetail.objects.filter(telephone__startswith='11').first()#找到以11開頭的電話號,獲取AuthorDetail 主鍵對象 print(author_obj.author.book_set.all().values('title','publish__name'))#獲取該條件所對應的作者,獲取所有書籍對象信息,通過values取值。 #方式2 ret =Book.objects.filter(authors__authorDetail__telephone__startswith='11').values('title','publish__name') print(ret)
聚合查詢與分組查詢(很重要!!!)
聚合查詢:aggregate(*args, **kwargs),只對一個組進行聚合
#查詢 所有書籍價格的平均值
print(Book.objects.all().aggregate(Avg('price')))
aggregate()是QuerySet 的一個終止子句(也就是返回的不再是一個QuerySet集合的時候),意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。
print(Book.objects.all().aggregate(avgprice =Avg('price')))
如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢:
print(Book.objects.all().aggregate(Avg('price'),Max("price"),Min("price"))) #輸出 {'price__avg': 186.222222, 'price__max': Decimal('200.00'), 'price__min': Decimal('100.00')}
分組查詢 :
annotate():為QuerySet中每一個對象都生成一個獨立的匯總值。
是對分組完之后的結果進行的聚合
#統計每一本書的作者數量 #方式一 print(Book.objects.all().annotate(authorNum = Count('authors__name')).values('authorNum')) #方式二 book_list = Book.objects.all().annotate(authorNum = Count('authors__name')) for book_obj in book_list: print(book_obj.title,book_obj.authorNum)
#統計每一個出版社最便宜的書
#方式一 print(Book.objects.values('publish__name').annotate(MinPrice=Min('price'))) #方式二 print(Publish.objects.all().annotate(minprice=Min('book__price')).values('name','minprice')) #方式三 publishlist = Publish.objects.annotate(minprice =Min('book__price')) for pub_obj in publishlist: print(pub_obj.name,pub_obj.minprice)
#打印以py開頭的書籍對應的作者個數
print(Book.objects.filter(title__startswith='py').annotate(au_count = Count('authors__name')).values('au_count'))
#根據一本書的作者數量多少對查詢集QuuerySet進行排序
print(Book.objects.all().annotate(Num=Count('authors__name')).order_by('Num').values('title'))
#獲取book所有對象,創建分組Num 統計作者的數量,通過order_by進行排序, values取值
#查詢各個作者出的書的總價格
#方式一
print(Author.objects.all().annotate(pricesum = Sum('book__price')).values('name','pricesum'))
#方式二
print(Book.objects.values('authors__name').annotate(pricesum = Sum('price')).values('authors__name','pricesum'))
F與Q查詢
F查詢:
在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢?
Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值。
#查詢書籍價格小於100元並且作者年齡=2017的書
print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))
#修改也可以使用F函數,比如說 書籍ID為4的書籍價格上漲100元
print(Book.objects.filter(nid=4).update(price=F('price')+100))
#Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操作。
#查詢書記價格小於作者年齡*2的書籍
print(Book.objects.filter(price__lt=F('authors__age')*2).values('title'))
Q查詢:
filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用Q 對象。
# 查詢書籍價格小於100元並且作者年份等於2017的書
print(Book.objects.filter(Q(price__lt='100')&Q(authors__authorDetail__birthday__year=2017)).values('title'))
#查詢評論數大於100或者閱讀數小於200的書
print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200))) Q 對象可以使用& 和| 操作符組合起來。當一個操作符在兩個Q 對象上使用時,它產生一個新的Q 對象。
#查詢年份等於2017年或者價格大於200的書
print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)))
#查詢年份不是2017年或者價格大於200的書
print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200)))
與:&Q
或:|Q
非:~Q
注意:
bookList=models.Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python" )
查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。例如: