(轉)python 全棧開發,Day74(基於雙下劃線的跨表查詢,聚合查詢,分組查詢,F查詢,Q查詢)
昨日內容回顧
# 一對多的新增方式1(推薦) # book=Book.objects.create(title="水滸傳",price=100,pub_date="1643-4-12",publish_id=1) # print(book.title) # 一對多的新增方式2 # publish必須接受一個物件 # xigua=Publish.objects.filter(name="西瓜出版社").first() # book=Book.objects.create(title="三國演義",price=300,pub_date="1643-4-12",publish=xigua)View Code# print(book.title) # ### # book=Book(title="三國演義",price=300,pub_date="1643-4-12",publish=xigua) # book.save() # # # ### # book=Book() # book.title="三國演義" # book.price=300 # book.pub_date="1643-4-12" # book.publish=xigua # book.publish # 與這本書籍關聯的出版社物件 # print(book.publish) # print(book.publish_id) ################################################################################ # 新增多對多關係 # book=Book.objects.create(title="python葵花寶典",price=122,pub_date="2012-12-12",publish_id=1) # alex=Author.objects.filter(name="alex").first() # egon=Author.objects.filter(name="egon").first() # jinxin=Author.objects.filter(name="jinxin").first()# book.publish=xigua # book.authors.add(alex,egon) # book.authors.add(*[alex,egon]) # author_list=Author.objects.all() # [obj,.....] # book.authors.add(*author_list) # 解除繫結的關係 # book=Book.objects.filter(id=4).first() # book.authors.clear() # book.authors.add(jinxin) # book.authors.set([jinxin,]) # book.authors.remove(alex,egon) # book.authors.remove(*[alex,egon]) # book.authors.clear() # book.authors.add(1,2) # book.authors.add(*[1,2]) ############################################# # print(book.publish) # 與這本書籍關聯的出版社物件 # print(book.authors.all()) # 與這本書關聯的作者物件queryset物件集合 <QuerySet [<Author: alex>, <Author: egon>, <Author: jinxin>]> # ############################################# # print(book.authors) # app01.Author.None # print(type(book.authors)) # print(book.pub_date) # 2012-12-12 2012-12-12 00:00:00+00:00 # print(type(book.pub_date)) # <class 'datetime.date'> <class 'datetime.datetime'>
一、基於雙下劃線的跨表查詢
Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯絡。要做跨關係查詢,就使用兩個下劃線來連結模型(model)間關聯欄位的名稱,直到最終連結到你想要的model 為止。
核心得學會通知ORM引擎什麼時候,join哪張表
join看似複雜,實則最簡單。因為把欄位列出來之後,就相當於單表操作了!想怎麼取值都可以!
正向查詢按欄位,反向查詢按表名小寫用來告訴ORM引擎join哪張表
返回值是QuerySet
一對多:
正向查詢
返回結構是queryset()
正向查詢:關聯屬性在book表中,所以book物件找關聯出版社物件,正向查詢
反向查詢:關聯屬性在book表中,所以publish物件找關聯書籍,反向查詢
按欄位:xx book ------------------ > publish <-------------------- 按表名小寫__欄位名。比如publish__name
舉例:查詢西遊記這本書的出版社名字
先使用原生sql查詢
SELECT app01_publish.name from app01_book INNER JOIN app01_publish on app01_book.publish_id = app01_publish.id WHERE app01_book.title = '西遊記'View Code
執行結果為:榴蓮出版社
它的步驟為:先連表,再過濾
使用orm引擎查詢
修改settings.py,最後一行新增。表示開啟日誌
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }View Code
修改urls.py,增加路徑query
urlpatterns = [ path('admin/', admin.site.urls), path('add/', views.add), path('query/', views.query), ]View Code
修改views.py,增加query檢視函式
def query(request): ret = Book.objects.filter(title='西遊記').values("publish__name") print(ret)View Code
解釋:
Book.objects 表示基礎表,它是鏈式程式設計的開始。
publish__name 表示publish表下的name欄位。因為要的name欄位,它不在Book表中。那麼指定外部表示,需要加雙下劃線。注意:此表必須要和Book表有關聯!
訪問url:http://127.0.0.1:8000/query/
檢視Pycharm控制檯,輸出:
<QuerySet [{'publish__name': '榴蓮出版社'}]> (0.000) SELECT "app01_publish"."name" FROM "app01_book" INNER JOIN "app01_publish" ON ("app01_book"."publish_id" = "app01_publish"."id") WHERE "app01_book"."title" = '西遊記' LIMIT 21; args=('西遊記',)View Code
可以看出,ORM執行的sql和手寫的sql,大致是一樣的!
反向查詢
舉例:查詢出版過西遊記的出版社
def query(request): ret = Publish.objects.filter(book__title="西遊記").values("name") print(ret) return HttpResponse('查詢成功')View Code
解釋:
book__title 表示book表中的title欄位,它不需要加引號
values("name") 表示publish表的中name欄位,為什麼呢?因為基礎表示publish,它可以直接取name欄位!
重新整理頁面,檢視控制檯輸出:
(0.001) SELECT "app01_publish"."name" FROM "app01_publish" INNER JOIN "app01_book" ON ("app01_publish"."id" = "app01_book"."publish_id") WHERE "app01_book"."title" = '西遊記' LIMIT 21; args=('西遊記',) <QuerySet [{'name': '榴蓮出版社'}]>View Code
查詢結果上面的例子是一樣的。
多對多查詢
正向查詢:關聯屬性在book表中,所以book物件找關聯作者集合,正向查詢
反向查詢:關聯屬性在book表中,所以author物件找關聯書籍,反向查詢
正 按欄位:xx book ------------------------- > author <------------------------- 反 按表名小寫__欄位名
正向查詢
舉例:查詢西遊記這本書籍的所有作者的姓名和年齡
先用原生sql查詢
SELECT app01_author.name,app01_author.age from app01_book INNER JOIN app01_book_authors on app01_book_authors.book_id = app01_book.id INNER JOIN app01_author on app01_book_authors.author_id = app01_author.id WHERE app01_book.title = '西遊記'View Code
涉及到3表查詢,查詢結果為:
使用orm引擎查詢
def query(request): ret = Book.objects.filter(title="西遊記").values("authors__name","authors__age") print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'authors__name': 'xiao', 'authors__age': 25}]> (0.001) SELECT "app01_author"."name", "app01_author"."age" FROM "app01_book" LEFT OUTER JOIN "app01_book_authors" ON ("app01_book"."id" = "app01_book_authors"."book_id") LEFT OUTER JOIN "app01_author" ON ("app01_book_authors"."author_id" = "app01_author"."id") WHERE "app01_book"."title" = '西遊記' LIMIT 21; args=('西遊記',)View Code
解釋:
由於book表和author表是多對多關係,所以使用ORM查詢時,它會自動對應關係
authors__name 表示author表的name欄位
那麼使用ORM處理多表,就顯得很簡單了!
反向查詢
還是上面的需求,以author為基礎表查詢
def query(request): ret = Author.objects.filter(book__title="西遊記").values("name","age") print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'name': 'xiao', 'age': 25}]> (0.002) SELECT "app01_author"."name", "app01_author"."age" FROM "app01_author" INNER JOIN "app01_book_authors" ON ("app01_author"."id" = "app01_book_authors"."author_id") INNER JOIN "app01_book" ON ("app01_book_authors"."book_id" = "app01_book"."id") WHERE "app01_book"."title" = '西遊記' LIMIT 21; args=('西遊記',)View Code
執行結果同上!
一對一
正向查詢:關聯屬性在authordetail表中,所以author物件找關聯作者詳情,正向查詢
反向查詢:關聯屬性在author表中,所以authordetail物件找關聯作者資訊,反向查詢
正向: 按欄位:.ad author ------------------------- > authordetail <------------------------- 反向: 按表名小寫 authordetail_obj.author
正向查詢
舉例:查詢xiao的女朋友名字
def query(request): ret = Author.objects.filter(name="xiao").values("ad__gf") print(ret) return HttpResponse('查詢成功')View Code
解釋:ORM查詢時,會自動對應關係。ad__gf表示authordetail表的gf欄位
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'ad__gf': '趙麗穎'}]> (0.001) SELECT "app01_authordetail"."gf" FROM "app01_author" INNER JOIN "app01_authordetail" ON ("app01_author"."ad_id" = "app01_authordetail"."id") WHERE "app01_author"."name" = 'xiao' LIMIT 21; args=('xiao',)View Code
反向查詢
還是上面的需求,以authordetail表為基礎表查詢
def query(request): ret = AuthorDetail.objects.filter(author__name="xiao").values("gf") print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'gf': '趙麗穎'}]> (0.002) SELECT "app01_authordetail"."gf" FROM "app01_authordetail" INNER JOIN "app01_author" ON ("app01_authordetail"."id" = "app01_author"."ad_id") WHERE "app01_author"."name" = 'xiao' LIMIT 21; args=('xiao',)View Code
進階練習(連續跨表)
舉例1
查詢榴蓮出版社出版過的所有書籍的名字以及作者的姓名
正向查詢
def query(request): ret = Book.objects.filter(publish__name="榴蓮出版社").values_list("title","authors__name") print(ret) return HttpResponse('查詢成功')View Code
解釋:
book表示連接出版社和作者的核心表。以它為基礎表查詢,比較好處理!
publish__name 表示publish表的name欄位。
authors__name表示book_authors表(book和author的關係表)的name欄位。
重新整理頁面,檢視控制檯輸出:
<QuerySet [('西遊記', 'xiao')]> (0.000) SELECT "app01_book"."title", "app01_author"."name" FROM "app01_book" INNER JOIN "app01_publish" ON ("app01_book"."publish_id" = "app01_publish"."id") LEFT OUTER JOIN "app01_book_authors" ON ("app01_book"."id" = "app01_book_authors"."book_id") LEFT OUTER JOIN "app01_author" ON ("app01_book_authors"."author_id" = "app01_author"."id") WHERE "app01_publish"."name" = '榴蓮出版社' LIMIT 21; args=('榴蓮出版社',)View Code
反向查詢
還是上面的需求,以publish表為基礎表查詢
def query(request): ret = Publish.objects.filter(name="榴蓮出版社").values_list("book__title","book__authors__age","book__authors__name") print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
<QuerySet [('西遊記', 25, 'xiao')]> (0.001) SELECT "app01_book"."title", "app01_author"."age", "app01_author"."name" FROM "app01_publish" LEFT OUTER JOIN "app01_book" ON ("app01_publish"."id" = "app01_book"."publish_id") LEFT OUTER JOIN "app01_book_authors" ON ("app01_book"."id" = "app01_book_authors"."book_id") LEFT OUTER JOIN "app01_author" ON ("app01_book_authors"."author_id" = "app01_author"."id") WHERE "app01_publish"."name" = '榴蓮出版社' LIMIT 21; args=('榴蓮出版社',)View Code
舉例2
手機號以11開頭的作者出版過的所有書籍名稱以及出版社名稱
提示:涉及到5表查詢!
先使用原生sql查詢
SELECT app01_book.title,a01p.name from app01_book INNER JOIN app01_book_authors as a on app01_book.id = a.book_id INNER JOIN app01_author as a3 on a.author_id = a3.id INNER JOIN app01_authordetail as a2 on a3.ad_id = a2.id INNER JOIN app01_publish as a01p on app01_book.publish_id = a01p.id WHERE a2.tel like '11%'View Code
執行結果:
使用orm引擎查詢
正向查詢:
def query(request): ret = Book.objects.filter(authors__ad__tel__startswith="11").values("title","publish__name") print(ret) return HttpResponse('查詢成功')View Code
解釋:
authors__ad__tel 表示book_authors表,author表的ad欄位,authordetail表的tel欄位,做關聯查詢。
__startswith 表示以什麼開頭,它會使用like查詢,比如'11%'
"title","publish__name" 分別表示book表的title欄位,publish表的name欄位
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'title': 'python', 'publish__name': '西瓜出版社'}, {'title': 'python', 'publish__name': '西瓜出版社'}, {'title': 'python', 'publish__name': '西瓜出版社'}, {'title': '西遊記', 'publish__name': '榴蓮出版社'}, {'title': '三國演義', 'publish__name': '西瓜出版社'}]> (0.001) SELECT "app01_book"."title", "app01_publish"."name" FROM "app01_book" INNER JOIN "app01_book_authors" ON ("app01_book"."id" = "app01_book_authors"."book_id") INNER JOIN "app01_author" ON ("app01_book_authors"."author_id" = "app01_author"."id") INNER JOIN "app01_authordetail" ON ("app01_author"."ad_id" = "app01_authordetail"."id") INNER JOIN "app01_publish" ON ("app01_book"."publish_id" = "app01_publish"."id") WHERE "app01_authordetail"."tel" LIKE '11%' ESCAPE '\' LIMIT 21; args=('11%',)View Code
反向查詢:
def query(request): ret = Author.objects.filter(ad__tel__startswith="11").values("book__title","book__publish__name") print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
<QuerySet [{'book__publish__name': '西瓜出版社', 'book__title': 'python'}, {'book__publish__name': '榴蓮出版社', 'book__title': '西遊記'}, {'book__publish__name': '西瓜出版社', 'book__title': 'python'}, {'book__publish__name': '西瓜出版社', 'book__title': '三國演義'}, {'book__publish__name': '西瓜出版社', 'book__title': 'python'}]> (0.001) SELECT "app01_book"."title", "app01_publish"."name" FROM "app01_author" INNER JOIN "app01_authordetail" ON ("app01_author"."ad_id" = "app01_authordetail"."id") LEFT OUTER JOIN "app01_book_authors" ON ("app01_author"."id" = "app01_book_authors"."author_id") LEFT OUTER JOIN "app01_book" ON ("app01_book_authors"."book_id" = "app01_book"."id") LEFT OUTER JOIN "app01_publish" ON ("app01_book"."publish_id" = "app01_publish"."id") WHERE "app01_authordetail"."tel" LIKE '11%' ESCAPE '\' LIMIT 21; args=('11%',)View Code
二、聚合查詢
聚合 是aggreate(*args,**kwargs),通過QuerySet 進行計算。做求值運算的時候使用
主要有Sum,Avg,Max,Min。使用前,需要匯入模組
from django.db.models import Sum,Avg,Max,Min,Count
Sum (總和)
返回值是一個字典
舉例:查詢所有書籍的總價格
def query(request): ret = Book.objects.all().aggregate(Sum("price")) print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
{'price__sum': Decimal('522.00')} (0.001) SELECT CAST(SUM("app01_book"."price") AS NUMERIC) AS "price__sum" FROM "app01_book"; args=() [02/Jul/2018 21:45:26] "GET /query/ HTTP/1.1" 200 12View Code
Avg (平均值)
返回值是一個字典
舉例:查詢所有書籍的平均價格
使用原生sql查詢
select avg(price) from app01_book
執行輸出:174
使用orm引擎查詢
def query(request): ret = Book.objects.all().aggregate(Avg("price")) print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
(0.001) SELECT AVG("app01_book"."price") AS "price__avg" FROM "app01_book"; args=() [02/Jul/2018 21:38:42] "GET /query/ HTTP/1.1" 200 12 {'price__avg': 174.0}View Code
aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的識別符號,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函式的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。
對返回的key起別名
def query(request): ret = Book.objects.all().aggregate(avg = Avg("price")) print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
{'avg': 174.0} (0.001) SELECT AVG("app01_book"."price") AS "avg" FROM "app01_book"; args=()View Code
Max 和Min
返回值是一個字典
如果你希望生成不止一個聚合,你可以向aggregate()子句中新增另一個引數。
舉例:所有圖書價格的最大值和最小值
def query(request): ret = Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
{'price__avg': 174.0, 'price__min': Decimal('100.00'), 'price__max': Decimal('300.00')} (0.001) SELECT AVG("app01_book"."price") AS "price__avg", CAST(MIN("app01_book"."price") AS NUMERIC) AS "price__min", CAST(MAX("app01_book"."price") AS NUMERIC) AS "price__max" FROM "app01_book"; args=()View Code
Count (統計結果行數)
舉例:查詢西瓜出版社總共出版過多少本書籍
def query(request): ret = Book.objects.all().aggregate(count = Count("id")) print(ret) return HttpResponse('查詢成功')View Code
重新整理頁面,檢視控制檯輸出:
{'count': 3} (0.001) SELECT COUNT("app01_book"."id") AS "count" FROM "app01_book"; args=()View Code
三、分組查詢
分組:將查詢結果按照某個欄位或多個欄位進行分組。欄