文章目錄
- 1. QuerySet被計算的場合
- 2. queryset使用實戰(以Blog爲例)
- `我們測試一下下面這些語句執行的sql,需要在settings里加入 LOGGING(驗證方式)`
- 1. 我們使用`posts = Blog.objects.all() 或者 posts = Blog.objects.filter()
- 2. queryset是具有cache的
- 3. 簡單的使用if語句進行判斷也會完全執行整個queryset並且把數據放入cache,爲了避免這個,可以用exists()方法來檢查是否有數據:
- 4. 當queryset非常巨大時,cache會成爲問題
- 3. 總結
- 4. 開啓數據庫的logging
簡言: Django的QuerySet具有
延遲
特性,僅在強制操作下才會被執行,這種行爲使得QuerySet更加的高效我們可以連接任意一個過濾器到一個QuerySet上,在QuerySet
計算之前
並不會訪問數據庫
1. QuerySet被計算的場合
- 首次迭代時
- 當對QuerySet訪問時,如Post.objects.all()[:4]
- 當對QuerySet緩存時
- 當在QuerySet上調用repr()或len()時
- 當在QuerySet上顯示調用list()時
- 當在某個語句中對QuerySet進行測試時,如bool(), or, and 或 if
2. queryset使用實戰(以Blog爲例)
我們測試一下下面這些語句執行的sql,需要在settings里加入 LOGGING(驗證方式)
1. 我們使用`posts = Blog.objects.all() 或者 posts = Blog.objects.filter()
- 上面的代碼並沒有運行任何的數據庫查詢。你可以使用posts,給它加上一些過濾條件,或者將它傳給某個函數,這些操作都不會發送給數據庫。這是對的,因爲數據庫查詢是顯著影響web應用性能的因素之一
2. queryset是具有cache的
-
當你遍歷queryset時,所有匹配的記錄會從數據庫獲取,然後轉換成Django的model。這被稱爲執行
(evaluation).這些model會保存在queryset內置的cache中,這樣如果你再次遍歷這個queryset,
你不需要重複運行通用的查詢。我們使用兩次for循環,可以看到logging只會打印一次posts = Post.published.all() for post in posts: # 執行sql查詢 print(post) print(post.publish) print(post.title) for post in posts: # 不執行sql查詢,使用cache print(post)
3. 簡單的使用if語句進行判斷也會完全執行整個queryset並且把數據放入cache,爲了避免這個,可以用exists()方法來檢查是否有數據:
posts = Post.published.all()
if posts.exists(): # .exists()判斷值是否存在,可以避免數據放入queryset的cache
for post in posts:
print(post)
if posts: # if判斷是否存在也會進行數據庫查詢並將返回結果放入QuerySet的cache
print(2333)
關於是否使用exists()的諫言:
當只是用來判斷某個QuerySet是否存在的時候,使用if QuerySet.exists()
,當用來判斷某個QuerySet是否存在,並且後面的語句還會用到QuerySet中的數據的時候使用if QuerySet:
,使用.exists反而會造成額外的開銷
4. 當queryset非常巨大時,cache會成爲問題
處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統
進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可以使用iterator()方法
來獲取數據,處理完數據就將其丟棄。
posts = Post.published.all().iterator()
# iterator()可以一次只從數據庫獲取少量數據,這樣可以節省內存
for post in posts:
print(post)
print(post.publish)
# 但是,再次遍歷沒有打印,因爲迭代器已經在上一次遍歷(next)到最後一次了,沒有可以遍歷得了
for post in posts:
print(post)
使用iterator()方法來防止生成cache,意味着遍歷同一個queryset時會重複執行查詢。所以使
用iterator()的時候要當心,確保你的代碼在操作一個大的queryset時沒有重複執行查詢
posts = Post.published.all()
for post in posts.iterator(): # 此處會進行一次sql查詢,但是並沒有緩存進queryset cache
print(post)
print(post.publish)
for post in posts.iterator(): # 此處會再次進行一次sql查詢,同樣沒有緩存進queryset cache
print(post)
3. 總結
- queryset的cache是用於減少程序對數據庫的查詢,在通常的使用下會保證只有在需要的時候纔會查詢數據庫。
- 使用exists()和iterator()方法可以優化程序對內存的使用。不過,由於它們並不會生成queryset cache,可能會造成額外的數據庫查詢。
4. 開啓數據庫的logging
在setting中加入
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}