1. 程式人生 > >(轉)python 全棧開發,Day74(基於雙下劃線的跨表查詢,聚合查詢,分組查詢,F查詢,Q查詢)

(轉)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)
# 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'>
View Code

 

一、基於雙下劃線的跨表查詢

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 12
View 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

 

三、分組查詢

分組:將查詢結果按照某個欄位或多個欄位進行分組。欄