Django之QuerySet 查詢


首先來看下如何查詢。我們在網頁中增加書名的查詢鏈接

后端的查詢處理代碼:這里由於authors是manytomanyfiled,因此我們這里用r.authors.all().first()來查詢符合條件的第一個

def books_inquery_result(request):
ret=[]
if request.method == 'POST':
bookname=request.POST['bookname']
result=Book.objects.filter(title=bookname)
for r in result:
ret.append(r.authors.all().first())
return HttpResponse(ret)

但是當輸入書名flask1的時候,我們查出來的確實下面這些代碼。這是為什么呢。

我們回頭看下之前添加books的操作:

ret3.publisher=Publisher.objects.all()[1]
ret3.publish_date=publish_date
ret3.save()
ret3.authors.add(Author.objects.all()[1])

我們查詢對應的Author.objects.all()[1]發現也是同樣的結果。

def result2(request):
result=Author.objects.all()[1]
return HttpResponse(result)

但是我們想要到的是書的作者和在網頁上輸入的對象關聯起來。那么我們添加的代碼就需要更新如下:這樣就把之前的輸入給加了進來

ret3=Book()
ret3.title=title
ret3.publisher=Publisher.objects.get(name=publish)
ret3.publish_date=publish_date
ret3.save()
ret3.authors.add(Author.objects.get(email=email))

此時再進行查詢,就可以得到正確的結果了。而且我們還可以通過

r.authors.all().first().email
r.authors.all().first().first_name
r.authors.all().first().last_name
接下來看下如何查詢出版商的信息,出版商是ForeignKey的關系。因此查詢也很簡單,通過下面的方式就可以查出出版商的信息。
r.publisher.address 
r.publisher. name
r.publisher. city
r.publisher. state_province
r.publisher. country
r.publisher. website

select_related查詢優化,首先我們在setting.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',
        },
    }
}


這個logging的配置作用是在終端查詢的時候自動打印出sql語句,這樣我們就能很直觀的看到查詢的方式。比如前面的Book查詢。我們來看下
Python manage.py shell
In [1]: from site_prj.models import Publisher,Book,Author
 
In [2]: books=Book.objects().all
In [4]: b1=books[0]
(0.002) SELECT "site_prj_book"."id", "site_prj_book"."title", "site_prj_book"."publisher_id", "site_prj_book"."publish_date" FROM "site_prj_book" LIMIT 1; args=()
In [6]: b1.title
Out[6]: u'flask'
 
In [7]: b1.publisher
(0.000) SELECT "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address", "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_
publisher"."website" FROM "site_prj_publisher" WHERE "site_prj_publisher"."id" = 2; args=(2,)
Out[7]: <Publisher: æ
我們看到在查詢publisher的時候又調用了sql語句,原因在於Book中publisher 和Book的關系是ForeignKey的關系,也就是多對一的關系。可能一本書有多個出版商都在出版,那么我們能不能在查詢的時候將這些出版商全都查出來呢。這就需要用到select_related的方法了。
ret2=Book.objects.all().select_related('publisher')  #得到所有的文章
r2=ret2[10]  #選擇第10篇
return HttpResponse(r2.publisher.state_province)  #得到出版次文章的所有出版商的省份
 
In [9]: book2=Book.objects.all().select_related('publisher')
 
In [10]: b2=book2[2]
(0.000) SELECT "site_prj_book"."id", "site_prj_book"."title", "site_prj_book"."publisher_id", "site_prj_book"."publish_date", "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address",
 "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_publisher"."website" FROM "site_prj_book" INNER JOIN "site_prj_publisher" ON ("site_prj_book"."publish
er_id" = "site_prj_publisher"."id") LIMIT 1 OFFSET 2; args=()
In [11]: b2.publisher.name
Out[11]: u'\xe6\x88\x90\xe9\x83\xbd\xe6\x97\xa5\xe6\x8a\xa5'
 
從下面的調試可以看到,沒有再進行SQL查詢。

那么我們現在來看下發現查詢。什么是反向查詢呢,之前的查詢是引用方查詢被引用方

比如在Book中publisher 和Book的關系是ForeignKey的關系,也就是多對一的關系.如果我想查出這本書的作者還寫了哪些書。就需要用到反向查詢。
代碼如下:
author=Author.objects.filter(first_name=first,last_name=second)
for a in author:
ret.append(a.book_set.all().first())
得到每個作者的對象后用author.book_set.all().first()得到該作者寫過的所有書。
同樣的通過輸入出版商也可以查出該出版商的所有書籍。
publish=Publisher.objects.filter(name=publishname)
for p in publish:
ret_publish.append(a.book_set.all().first())


下面介紹下Values獲取字典形式的結果。
def result3(request):
ret=[]
result=Book.objects.values('title','authors','publisher','publish_date')
for r in result:
ret.append(r)
return HttpResponse(ret)
這樣能將所有的查詢結果以字典的形式呈現出來。我們看到其中publisher和authro都是數字。而不是具體的名稱。

我們來看下對應的sql語句:可以看到在sql語句中就是選用的是Publisher和Author的id作為查詢條件且使用的是左鏈接的方式。
SELECT "site_prj_book"."title", "site_prj_book_authors"."author_id", "site_prj_book"."publisher_id", "site_prj_book"."publish_date" FROM "site_prj_book" LEFT OUTER JOIN "site_prj_book_authors" ON ("site_prj_book"."id" = "site_prj_book_authors"."book_id"); args=()
如果不想看字典的形式,而只是想看列表的方式,可以用values_list的方式
result=Book.objects.values_list('title','authors','publisher','publish_date')

Annotate進行聚合,計數,平均數,求和等
下面首先來計算作者的個數:
首先引入模塊:django.db.models里面包含所有的計算函數
from django.db.models import *
def result2(request):
result=Author.objects.all().values('email').annotate(count=Count('email')).values('email','count')
return HttpResponse(result)
這個函數的作用是統計作者中的email使用總次數。計每個作者的email出現的次數。Annotate代表以某個元素進行聚類的意思。這里是以每個名字的次數進行聚類。返回結果如下。
我們還可以通過聚類求平均,但是目前的表中沒有整數字段。我們在Author中添加一個age的字段。添加方法如下,進入python manage.py shell執行如下的語句。
In [1]: from django.db import connection
 
In [2]: cursor=connection.cursor()
 
In [3]: cursor.execute('Alter TABLE Authro add age Integer')
In [8]: cursor.execute('Alter TABLE site_prj_Author add age Integer')
(0.014) Alter TABLE site_prj_Author add age Integer; args=None
Out[8]: <django.db.backends.sqlite3.base.SQLiteCursorWrapper at 0x3050a80>
In [3]: print Author.objects.all().query
SELECT "site_prj_author"."id", "site_prj_author"."first_name", "site_prj_author"."last_name", "site_prj_author"."email", "site_prj_author"."age" FROM "site_prj_author"
 
添加后我們就可以通過下面的方式進行作者平均年齡的計算了                                                                                                                          
result=Author.objects.all().values('age').annotate(average=Avg('age')).values('age','average')
對應的SQL語句:
SELECT "site_prj_author"."age", AVG("site_prj_author"."age") AS "average" FROM "site_prj_author" GROUP BY "site_prj_author"."age"; args=()
 
鏈式查詢:
過濾成都日報出版且城市是成都的出版商
result=Publisher.objects.filter(name='成都日報').filter(city='成都')
過濾成都出版社且城市不是成都出版商
result=Publisher.objects.filter(name='成都日報').exclude(city='成都')
對應的SQL語句:
SELECT "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address", "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_publisher"."website" FROM "site_prj_publisher" WHERE (NOT ("site_prj_publisher"."name" = '成都日報') AND "site_prj_publisher"."city" = '成都');
 
Defer排除不需要的字段:有些模型的字段太多,我們只想看其中幾種可以用defer來排查某些字段
result=Author.objects.all().defer('age').query
對應的SQL:可以看到沒有查詢age字段
SELECT "site_prj_author"."id", "site_prj_author"."first_name", "site_prj_author"."last_name", "site_prj_author"."email" FROM "site_prj_author"
 
相對應的only就是選擇需要的字段。
result=Author.objects.all().only('age').query
對應的SQL:
SELECT "site_prj_author"."id", "site_prj_author"."age" FROM "site_prj_author"
 
最后補充model的所有類型描述:

AutoField:一個自動遞增的整型字段,添加記錄時它會自動增長。你通常不需要直接使用這個字段;如果你不指定主鍵的話,系統會自動添加一個主鍵字段到你的model。(參閱自動主鍵字段)

BooleanField:布爾字段,管理工具里會自動將其描述為checkbox。

CharField:字符串字段,單行輸入,用於較短的字符串,如要保存大量文本, 使用 TextField,CharField有一個必填參數:

TextField:一個容量很大的文本字段, admin 管理界面用 <textarea>多行編輯框表示該字段數據。

CommaSeparatedIntegerField:用於存放逗號分隔的整數值。類似 CharField,必須maxlength 參數。

DateField:日期字段,admin 用一個文本框 <input type=”text”> 來表示該字段數據(附帶一個 JavaScript 日歷和一個”Today”快捷按鍵。有下列額外的可選參數:

auto_now:當對象被保存時,自動將該字段的值設置為當前時間.通常用於表示 “last-modified” 時間戳;
auto_now_add:當對象首次被創建時,自動將該字段的值設置為當前時間.通常用於表示對象創建時間。

FilePathField:選擇指定目錄按限制規則選擇文件,有三個參數可選, 其中”path”必需的,這三個參數可以同時使用, 參數描述:

path:必需參數,一個目錄的絕對文件系統路徑。 FilePathField 據此得到可選項目。 Example: “/home/images”;
match:可選參數, 一個正則表達式, 作為一個字符串, FilePathField 將使用它過濾文件名。 注意這個正則表達式只會應用到 base filename 而不是路徑全名。 Example: “foo。*\。txt^”, 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif;
recursive:可選參數, 是否包括 path 下全部子目錄,True 或 False,默認值為 False。

FloatField:浮點型字段。 必須提供兩個 參數, 參數描述:

max_digits:總位數(不包括小數點和符號)
decimal_places:小數位數。如:要保存最大值為 999 (小數點后保存2位),你要這樣定義字段:models.FloatField(…,max_digits=5, decimal_places=2),要保存最大值一百萬(小數點后保存10位)的話,你要這樣定義:models.FloatField(…,max_digits=19, decimal_places=10)

ImageField:類似 FileField, 不過要校驗上傳對象是否是一個合法圖片。它有兩個可選參數:height_field 和 width_field,如果提供這兩個參數,則圖片將按提供的高度和寬度規格保存。 該字段要求 Python Imaging 庫。

IntegerField:用於保存一個整數。

IPAddressField:一個字符串形式的 IP 地址, (如 “202.1241.30″)。

NullBooleanField:類似 BooleanField, 不過允許 NULL 作為其中一個選項。 推薦使用這個字段而不要用 BooleanField 加 null=True 選項。 admin 用一個選擇框 <select> (三個可選擇的值: “Unknown”, “Yes” 和 “No” ) 來表示這種字段數據。

PhoneNumberField:一個帶有合法美國風格電話號碼校驗的 CharField(格式:XXX-XXX-XXXX)。

PositiveIntegerField:類似 IntegerField, 但取值范圍為非負整數(這個字段應該是允許0值的…可以理解為無符號整數)

PositiveSmallIntegerField:

正小整型字段,類似 PositiveIntegerField, 取值范圍較小(數據庫相關)SlugField“Slug” 是一個報紙術語。 slug 是某個東西的小小標記(短簽), 只包含字母,數字,下划線和連字符。它們通常用於URLs。 若你使用 Django 開發版本,你可以指定 maxlength。 若 maxlength 未指定, Django 會使用默認長度: 50,它接受一個額外的參數:

SlugField:是一個報紙術語. slug 是某個東西的小小標記(短簽), 只包含字母,數字,下划線和連字符.它們通常用於URLs。

SmallIntegerField:類似 IntegerField, 不過只允許某個取值范圍內的整數。(依賴數據庫)

TimeField:時間字段,類似於 DateField 和 DateTimeField。

URLField:用於保存 URL。 若 verify_exists 參數為 True (默認), 給定的 URL 會預先檢查是否存在(即URL是否被有效裝入且沒有返回404響應)。

USStateField:美國州名縮寫,由兩個字母組成(天朝人民無視)。

XMLField:XML字符字段,校驗值是否為合法XML的 TextField,必須提供參數:

schema_path:校驗文本的 RelaxNG schema 的文件系統路徑。

 
        

 

 
        
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM