Django--QuerySet
一、QuerySet API 數據庫接口
從數據庫中查詢出來的結果一般是一個集合,這個集合叫做 QuerySet。
1. QuerySet 創建對象的方法:
一共有四種方法 # 方法 1 Author.objects.create(name="WeizhongTu", email="[email protected]") # 方法 2 twz = Author(name="WeizhongTu", email="[email protected]") twz.save() # 方法 3 twz = Author() twz.name="WeizhongTu" twz.email="[email protected]" twz.save() # 方法 4,首先嘗試獲取,不存在就創建,可以防止重復 Author.objects.get_or_create(name="WeizhongTu", email="[email protected]")# 返回值(object, True/False)
備註:前三種方法返回的都是對應的 object,最後一種方法返回的是一個元組,(object, True/False),創建時返回 True, 已經存在時返回 False
2. 獲取對象的方法(上一篇的部分代碼)
Person.objects.all() # 查詢所有 Person.objects.all()[:10] 切片操作,獲取10個人,不支持負索引,切片可以節約內存,不支持負索引,後面有相應解決辦法,第7條 Person.objects.get(name="WeizhongTu") # 名稱為 WeizhongTu 的一條,多條會報錯 get是用來獲取一個對象的,如果需要獲取滿足條件的一些人,就要用到filter Person.objects.filter(name="abc") # 等於Person.objects.filter(name__exact="abc") 名稱嚴格等於 "abc" 的人 Person.objects.filter(name__iexact="abc") # 名稱為 abc 但是不區分大小寫,可以找到 ABC, Abc, aBC,這些都符合條件 Person.objects.filter(name__contains="abc") # 名稱中包含 "abc"的人 Person.objects.filter(name__icontains="abc") #名稱中包含 "abc",且abc不區分大小寫 Person.objects.filter(name__regex="^abc") # 正則表達式查詢 Person.objects.filter(name__iregex="^abc")# 正則表達式不區分大小寫 # filter是找出滿足條件的,當然也有排除符合某條件的 Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person對象 Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名稱含有abc, 但是排除年齡是23歲的
3. 刪除符合條件的結果
和上面類似,得到滿足條件的結果,然後 delete 就可以(危險操作,正式場合操作務必謹慎),比如:
Person.objects.filter(name__contains="abc").delete() # 刪除 名稱中包含 "abc"的人 如果寫成 people = Person.objects.filter(name__contains="abc") people.delete() 效果也是一樣的,Django實際只執行一條 SQL 語句。
4. 更新某個內容
(1) 批量更新,適用於 .all() .filter() .exclude() 等後面 (危險操作,正式場合操作務必謹慎)
Person.objects.filter(name__contains="abc").update(name=‘xxx‘) # 名稱中包含 "abc"的人 都改成 xxx Person.objects.all().delete() # 刪除所有 Person 記錄
(2) 單個 object 更新,適合於 .get(), get_or_create(), update_or_create() 等得到的 obj,和新建很類似。
twz = Author.objects.get(name="WeizhongTu") twz.name="WeizhongTu" twz.email="[email protected]" twz.save() # 最後不要忘了保存!!!
5. QuerySet 是可叠代的,比如:
es = Entry.objects.all() for e in es: print(e.headline)
Entry.objects.all() 或者 es 就是 QuerySet 是查詢所有的 Entry 條目。
註意事項:
(1). 如果只是檢查 Entry 中是否有對象,應該用 Entry.objects.all().exists()
(2). QuerySet 支持切片 Entry.objects.all()[:10] 取出10條,可以節省內存
(3). 用 len(es) 可以得到Entry的數量,但是推薦用 Entry.objects.count()來查詢數量,後者用的是SQL:SELECT COUNT(*)
(4). list(es) 可以強行將 QuerySet 變成 列表
6. QuerySet 是可以用pickle序列化到硬盤再讀取出來的
>>> import pickle >>> query = pickle.loads(s) # Assuming ‘s‘ is the pickled string. >>> qs = MyModel.objects.all() >>> qs.query = query # Restore the original ‘query‘.
7. QuerySet 查詢結果排序
作者按照名稱排序
Author.objects.all().order_by(‘name‘) Author.objects.all().order_by(‘-name‘) # 在 column name 前加一個負號,可以實現倒序
8. QuerySet 支持鏈式查詢
Author.objects.filter(name__contains="WeizhongTu").filter(email="[email protected]") Author.objects.filter(name__contains="Wei").exclude(email="[email protected]") # 找出名稱含有abc, 但是排除年齡是23歲的 Person.objects.filter(name__contains="abc").exclude(age=23)
9. QuerySet 不支持負索引
Person.objects.all()[:10] 切片操作,前10條 Person.objects.all()[-10:] 會報錯!!! # 1. 使用 reverse() 解決 Person.objects.all().reverse()[:2] # 最後兩條 Person.objects.all().reverse()[0] # 最後一條 # 2. 使用 order_by,在欄目名(column name)前加一個負號 Author.objects.order_by(‘-id‘)[:20] # id最大的20條
10. QuerySet 重復的問題,使用 .distinct() 去重
一般的情況下,QuerySet 中不會出來重復的,重復是很罕見的,但是當跨越多張表進行檢索後,結果並到一起,可能會出來重復的值(我最近就遇到過這樣的問題)
qs1 = Pathway.objects.filter(label__name=‘x‘) qs2 = Pathway.objects.filter(reaction__name=‘A + B >> C‘) qs3 = Pathway.objects.filter(inputer__name=‘WeizhongTu‘) # 合並到一起 qs = qs1 | qs2 | qs3 這個時候就有可能出現重復的 # 去重方法 qs = qs.distinct()
二、
1. 查看 Django queryset 執行的 SQL
print str(User.objects.all().query)
print str(Author.objects.filter(name="WeizhongTu").query)
2. values_list 獲取元組形式結果
2.1 比如我們要獲取作者的 name 和 qq authors = Author.objects.values_list(‘name‘, ‘qq‘)
如果只需要 1 個字段,可以指定 flat=True Author.objects.values_list(‘name‘, flat=True)
3. values 獲取字典形式的結果
3.1 比如我們要獲取作者的 name 和 qq Author.objects.values(‘name‘, ‘qq‘) list(Author.objects.values(‘name‘, ‘qq‘))
3.2 查詢 twz915 這個人的文章標題 Article.objects.filter(author__name=‘twz915‘).values(‘title‘)
註意:
1. values_list 和 values 返回的並不是真正的 列表 或 字典,也是 queryset,他們也是 lazy evaluation 的(惰性評估,通俗地說,就是用的時候才真正的去數據庫查)
2. 如果查詢後沒有使用,在數據庫更新後再使用,你發現得到在是新內容!!!如果想要舊內容保持著,數據庫更新後不要變,可以 list 一下
3. 如果只是遍歷這些結果,沒有必要 list 它們轉成列表(浪費內存,數據量大的時候要更謹慎!!!)
4. extra 實現 別名,條件,排序等
extra 中可實現別名,條件,排序等,後面兩個用 filter, exclude 一般都能實現,排序用 order_by 也能實現。
別名: tags = Tag.objects.all().extra(select={‘tag_name‘: ‘name‘})
5. annotate 聚合 計數,求和,平均數等
5.1 計數
我們來計算一下每個作者的文章數(我們每個作者都導入的Article的篇數一樣,所以下面的每個都一樣)
Article.objects.all().values(‘author_id‘).annotate(count=Count(‘author‘)).values(‘author_id‘, ‘count‘)
5.2 求和 與 平均值
5.2.1 求一個作者的所有文章的得分(score)平均值
Article.objects.values(‘author_id‘).annotate(avg_score=Avg(‘score‘)).values(‘author_id‘, ‘avg_score‘)
5.2.2 求一個作者所有文章的總分
Article.objects.values(‘author__name‘).annotate(sum_score=Sum(‘score‘)).values(‘author__name‘, ‘sum_score‘)
6. select_related 優化一對一,多對一查詢
articles = Article.objects.all()[:10] 這樣會進行多次數據庫查詢。
articles = Article.objects.all().select_related(‘author‘)[:10] 這樣只進行一次數據庫查詢。
7. prefetch_related 優化一對多,多對多查詢
和 select_related 功能類似,但是實現不同。
select_related 是使用 SQL JOIN 一次性取出相關的內容。
prefetch_related 用於 一對多,多對多 的情況,這時 select_related 用不了,因為當前一條有好幾條與之相關的內容。
prefetch_related是通過再執行一條額外的SQL語句,然後用 Python 把兩次SQL查詢的內容關聯(joining)到一起
articles = Article.objects.all()[:3] 多次執行查詢語句
articles = Article.objects.all().prefetch_related(‘tags‘)[:3] 到第二條 SQL 語句,一次性查出了所有相關的內容。
8. defer 排除不需要的字段
在復雜的情況下,表中可能有些字段內容非常多,取出來轉化成 Python 對象會占用大量的資源。
這時候可以用 defer 來排除這些字段,比如我們在文章列表頁,只需要文章的標題和作者,沒有必要把文章的內容也獲取出來(因為會轉換成python對象,浪費內存)
Article.objects.all()
Article.objects.all().defer(‘content‘)
9. only 僅選擇需要的字段
和 defer 相反,only 用於取出需要的字段,假如我們只需要查出 作者的名稱
Author.objects.all().only(‘name‘) 使用only時一定包含主鍵id。
Django--QuerySet