1. 程式人生 > >Django--QuerySet

Django--QuerySet

led store 獲取對象 cor ati 計數 lua 內存 core

一、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