1. 程式人生 > >Django 2.1.3 文件-模型層-QuerySet 方法參考

Django 2.1.3 文件-模型層-QuerySet 方法參考

QuerySetAPI參考

譯自官方文件+自己的理解

本文件描述了QuerySetAPI 的詳細資訊。它建立在 模型資料庫查詢 指南中提供的材料之上,因此在閱讀本文件之前,您可能希望閱讀並理解這些文件。

在整個參考文獻中,我們將使用資料庫查詢指南中提供的示例Weblog模型。

1.什麼時候QuerySet求值

本質上,QuerySet可以構造,過濾,切片,並且通常可以在不實際訪問資料庫的情況下傳遞。在您對queryset求值之前,實際上不會發生任何資料庫活動。

您可以通過以下方式對QuerySet求值 :

(1)迭代。一個QuerySet是可迭代的,並且在您第一次迭代它時執行其資料庫查詢。例如,這將列印資料庫中所有書籍的名稱:

for e in Book.objects.all():
    print(e.name)

注意:如果您只想確定是否存在至少一個結果,請不要使用此選項。使用exists()效率更高。

>>>
Book.objects.exists() True

(2)切片。如 限制查詢集 中所述,可以使用Python的陣列切片語法對QuerySet進行切片。切片未評估的 QuerySet通常會返回另一個未評估的值QuerySet,但如果使用切片語法的“step”引數,Django將執行資料庫查詢,並返回一個列表。QuerySet對已經過評估的切片也會返回一個列表。

還要注意,即使對未評估的QuerySet切片返回另一個未評估QuerySet,進一步修改它(例如,新增更多過濾器,或修改排序)是不允許的,因為這不能很好地轉換為SQL,它也沒有明確的含義。

(3)醃製/快取。有關醃製QuerySets時所涉及的內容的詳細資訊,請參閱以下部分。對於本節而言,重要的是從資料庫中讀取結果。

(4)repr()。在QuerySet上呼叫repr()方法進行求值,這是為了方便Python互動式直譯器,因此您可以在互動式使用API​​時立即看到結果。

(5)len()。在QuerySet上呼叫len()方法進行求值. 正如您所料,這會返回結果列表的長度。

注意:如果您只需要確定集合中的記錄數(並且不需要實際物件),那麼使用SQL處理資料庫級別的計數方法SELECT COUNT(*)會更有效。Django 正是出於這個原因提供了一種count()方法。

(6)list()。在QuerySet上呼叫list()方法進行求值。
例子:

def fun05_queryset_api():
    print(Book.objects.exists())
    print(repr(Book.objects.all()))
    print(len(Book.objects.all()))
    print(Book.objects.all().count())
    print(list(Book.objects.all()))
# 輸出結果
<QuerySet [<Book: <吶喊>>, <Book: <哈利波特>>, <Book: <滅亡>>, <Book: <中國文人集>>]>
4
4
[<Book: <吶喊>>, <Book: <哈利波特>>, <Book: <滅亡>>, <Book: <中國文人集>>]

(7)bool()。測試QuerySet在布林上下文中,如使用 bool(),or,and或if語句,將導致查詢被執行。如果至少有一個結果,QuerySet則為 True,否則False。例如:

if Book.objects.filter(name__contains="吶"):
   print("There is at least one Book with the name contains 吶")

注意:如果您只想確定是否存在至少一個結果(並且不需要實際物件),則使用exists()會更有效。

2. 醃製(pickling)QuerySets

如果醃製 (pickle) 一個QuerySet,這將強制所有結果在醃製之前載入到記憶體中。醃製通常用作快取的前身,並且當重新載入快取的查詢集時,您希望結果已經存在並且可以使用(從資料庫讀取可能需要一些時間,從而無法實現快取)。這意味著當你取消醃製(unpickle)QuerySet,它包含被醃製時的結果,而不是當前在資料庫中的結果。

如果您只想醃製必要的資訊以便QuerySet稍後從資料庫重新建立 ,請醃製該QuerySet的query屬性。然後,您可以使用以下程式碼重新建立原始的QuerySet(沒有載入任何結果):

>>> import pickle
>>> query = pickle.loads(s)     # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query            # Restore the original 'query'.

該query屬性是一個不透明的物件。它表示查詢構造的內部,不是公共API的一部分。但是,如此處所述,對屬性的內容進行pickle和unpickle是安全的(並且完全支援)。

您無法在版本之間共享pickles資料

醃製 QuerySets僅對用於生成它們的Django版本有效。如果使用Django版本N生成pickle,則無法保證使用Django版本N + 1可以讀取pickle。醃製資料不應該用作長期存檔的策略。

由於pickle相容性錯誤很難診斷,例如無提示損壞的物件,因此當您嘗試在Django版本中取消查詢不同於其被pickle的查詢集時,會引發RuntimeWarning錯誤。

3. QuerySet API

這是一個正式的QuerySet宣告:

class QuerySet(model = None,query = None,using = None)原始碼
通常,當您與一個QuerySet進行互動時,您將通過 過濾器鏈 來使用它 。為了使其工作,大多數 QuerySet方法返回新的查詢集。本節稍後將詳細介紹這些方法。

該QuerySet 類有可用於內省的兩個公共屬性:

(1)ordered
如果QuerySet是有序的返回True - 即在模型上有一個order_by()子句或預設排序;否則返回False。

(2)db
現在執行此查詢時將使用的資料庫。

註解

QuerySet存在一個query引數,以便專門的查詢子類可以重建內部查詢狀態。引數的值是該查詢狀態的不透明表示,並且不是公共API的一部分。簡單地說:如果你需要問,你不需要使用它。

print(Book.objects.all().ordered)
print(Book.objects.order_by('name').ordered)
print(Book.objects.all().db)
print(Book.objects.all().query)
# 輸出結果
False
True
default
SELECT "filter_book"."id", "filter_book"."name", "filter_book"."price", "filter_book"."category", "filter_book"."publishs_id" FROM "filter_book"

3.1 返回新QuerySets的方法

Django提供了一系列QuerySet細化方法,可以修改由其返回QuerySet的結果型別或執行SQL查詢的方式。

filter()

filter(** kwargs)
返回一個QuerySet,包含與給定查詢引數匹配的所有物件。

查詢引數(**kwargs)應採用下面的欄位查詢中描述的格式 。多個引數通過AND在底層SQL語句中連線起來。

如果需要執行更復雜的查詢(例如,帶OR語句的查詢),則可以使用Q 物件

exclude()

exclude(** kwargs)
返回一個QuerySet,包含與給定查詢引數匹配的所有物件。

查詢引數(**kwargs)應採用下面的欄位查詢中描述的格式 。多個引數通過AND在底層SQL語句中連線起來,整個事件都包含在一個NOT()中。

此示例排除所有價格小於30並且書名包含“吶喊”的書籍:

print(Book.objects.exclude(price__lt=30,name__contains="吶喊"))
# 輸出結果
<QuerySet [<Book: <哈利波特>>, <Book: <滅亡>>, <Book: <中國文人集>>]>

在SQL術語中,轉化為:

SELECT ...
  WHERE
  NOT ("filter_book"."name" LIKE '%吶喊%' 
  AND "filter_book"."price" < 30)

此示例排除所有價格小於30或者書名包含“吶喊”的書籍::

print(Book.objects.exclude(price__lt=30).exclude(name__contains="吶喊"))
# 輸出結果
<QuerySet [<Book: <哈利波特>>, <Book: <中國文人集>>]>

在SQL術語中,評估為:

SELECT ...
FROM "filter_book"
WHERE (NOT ("filter_book"."price" < 30)
AND
NOT ("filter_book"."name" LIKE '%吶喊%' )

請注意,第二個示例更具限制性。

如果需要執行更復雜的查詢(例如,帶OR語句的查詢),則可以使用Q 物件

annotate()

annotate(* args,** kwargs)