QuerySet
API 參考¶
本文檔詳細描述了 QuerySet
API 的細節。它建立在 模型 和 資料庫查詢 指南中介紹的內容之上,因此您可能需要先閱讀並理解這些文檔,然後再閱讀本文檔。
在本參考中,我們將使用 資料庫查詢指南 中提供的 範例部落格模型。
何時評估 QuerySet
?¶
在內部,可以建構、篩選、切片和一般傳遞 QuerySet
,而無需實際存取資料庫。在您執行某些操作來評估 queryset 之前,不會實際發生資料庫活動。
您可以透過以下方式評估 QuerySet
:
迭代。
QuerySet
是可迭代的,它會在您第一次迭代時執行其資料庫查詢。例如,這將列印資料庫中所有條目的標題:for e in Entry.objects.all(): print(e.headline)
注意:如果您只想判斷是否存在至少一個結果,請勿使用此方法。使用
exists()
會更有效率。非同步迭代。 也可以使用
async for
迭代QuerySet
:async for e in Entry.objects.all(): results.append(e)
QuerySet 的同步和非同步迭代器共享相同的底層快取。
切片。 如 限制 QuerySet 中所述,可以使用 Python 的陣列切片語法來對
QuerySet
進行切片。對未評估的QuerySet
進行切片通常會傳回另一個未評估的QuerySet
,但是如果您使用切片語法的「step」參數,Django 將執行資料庫查詢並傳回清單。對已評估的QuerySet
進行切片也會傳回清單。另請注意,即使對未評估的
QuerySet
進行切片會傳回另一個未評估的QuerySet
,也不允許進一步修改它(例如,新增更多篩選器或修改排序),因為這無法很好地轉換為 SQL,而且也沒有明確的含義。封裝/快取。 有關 封裝 QuerySet 時所涉及的詳細資訊,請參閱以下章節。本節的重點是結果是從資料庫讀取的。
repr()。當您對
QuerySet
呼叫repr()
時,會評估QuerySet
。這是為了在 Python 互動式解譯器中提供便利,因此您可以在互動式使用 API 時立即看到結果。len()。當您對
QuerySet
呼叫len()
時,會評估QuerySet
。正如您可能預料的那樣,這會傳回結果清單的長度。注意:如果您只需要確定集合中的記錄數量(而不需要實際的物件),則使用 SQL 的
SELECT COUNT(*)
在資料庫層級處理計數會更有效率。Django 提供了count()
方法,正是為了這個目的。list()。 透過對
QuerySet
呼叫list()
來強制評估它。例如:entry_list = list(Entry.objects.all())
bool()。在布林內容中測試
QuerySet
,例如使用bool()
、or
、and
或if
陳述式,將會導致執行查詢。如果至少有一個結果,則QuerySet
為True
,否則為False
。例如:if Entry.objects.filter(headline="Test"): print("There is at least one Entry with the headline Test")
注意:如果您只想判斷是否存在至少一個結果(而不需要實際的物件),使用
exists()
會更有效率。
封裝 QuerySet
¶
如果您 pickle
QuerySet
,這將強制所有結果在封裝之前載入到記憶體中。封裝通常用作快取的前置步驟,並且當重新載入快取的 queryset 時,您希望結果已經存在並準備好使用(從資料庫讀取可能需要一些時間,這會損害快取的目的)。這表示當您解封裝 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 的一部分。但是,按照此處的描述來封裝和解封裝屬性的內容是安全的(且完全支援)。
QuerySet.values_list()
的限制
如果您使用封裝的 query
屬性重新建立 QuerySet.values_list()
,它將轉換為 QuerySet.values()
>>> import pickle
>>> qs = Blog.objects.values_list("id", "name")
>>> qs
<QuerySet [(1, 'Beatles Blog')]>
>>> reloaded_qs = Blog.objects.all()
>>> reloaded_qs.query = pickle.loads(pickle.dumps(qs.query))
>>> reloaded_qs
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
QuerySet
API¶
以下是 QuerySet
的正式宣告:
- class QuerySet(model=None, query=None, using=None, hints=None)[原始碼]¶
通常,當您與
QuerySet
互動時,您會透過 串聯篩選器 來使用它。為了使此方法運作,大多數QuerySet
方法都會傳回新的 queryset。這些方法將在本節後續詳細說明。QuerySet
類別具有以下可用於內省的公共屬性:- ordered[原始碼]¶
如果
QuerySet
已排序,則為True
,也就是說,它具有order_by()
子句,或模型上具有預設排序。否則為False
。
注意
QuerySet
的query
參數存在,以便專門的查詢子類別可以重建內部查詢狀態。參數的值是該查詢狀態的不透明表示,並非公共 API 的一部分。
返回新 QuerySet
的方法¶
Django 提供了一系列的 QuerySet
調整方法,這些方法會修改 QuerySet
返回的結果類型,或其 SQL 查詢的執行方式。
注意
這些方法不會執行資料庫查詢,因此可以安全地在非同步程式碼中執行,並且沒有單獨的非同步版本。
filter()
¶
- filter(*args, **kwargs)¶
返回一個新的 QuerySet
,其中包含符合給定查詢參數的物件。
查詢參數 (**kwargs
) 應採用下方 欄位查詢 中描述的格式。多個參數在底層 SQL 語句中會透過 AND
連接。
如果需要執行更複雜的查詢(例如,包含 OR
語句的查詢),可以使用 Q 物件
(*args
)。
exclude()
¶
- exclude(*args, **kwargs)¶
返回一個新的 QuerySet
,其中包含不符合給定查詢參數的物件。
查詢參數 (**kwargs
) 應採用下方 欄位查詢 中描述的格式。多個參數在底層 SQL 語句中會透過 AND
連接,並且整個內容會包含在 NOT()
中。
此範例排除所有 pub_date
晚於 2005-1-3 且 headline
為「Hello」 的所有條目
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline="Hello")
以 SQL 術語來說,這相當於
SELECT ...
WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
此範例排除所有 pub_date
晚於 2005-1-3 或標題為「Hello」的所有條目
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline="Hello")
以 SQL 術語來說,這相當於
SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'
請注意,第二個範例更具限制性。
如果需要執行更複雜的查詢(例如,包含 OR
語句的查詢),可以使用 Q 物件
(*args
)。
annotate()
¶
- annotate(*args, **kwargs)¶
使用提供的 查詢表達式 列表來註解 QuerySet
中的每個物件。表達式可以是簡單的值、模型上的欄位參考(或任何相關模型),或是對與 QuerySet
中的物件相關的物件計算出的彙總表達式(平均值、總和等)。
annotate()
的每個引數都是一個註解,將會新增到返回的 QuerySet
中的每個物件。
Django 提供的彙總函式在下方的 彙總函式 中描述。
使用關鍵字引數指定的註解將使用該關鍵字作為註解的別名。匿名引數將根據彙總函式的名稱和正在彙總的模型欄位產生別名。只有參考單一欄位的彙總表達式才能是匿名引數。其他所有內容都必須是關鍵字引數。
例如,如果正在操作部落格列表,您可能想確定每個部落格中有多少條目
>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count("entry"))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42
Blog
模型本身沒有定義 entry__count
屬性,但透過使用關鍵字引數來指定彙總函式,您可以控制註解的名稱
>>> q = Blog.objects.annotate(number_of_entries=Count("entry"))
# The number of entries on the first blog, using the name provided
>>> q[0].number_of_entries
42
有關彙總的深入討論,請參閱有關彙總的主題指南。
alias()
¶
- alias(*args, **kwargs)¶
與 annotate()
相同,但不是註解 QuerySet
中的物件,而是儲存表達式,以便稍後與其他 QuerySet
方法重複使用。當不需要表達式本身的結果,但它用於篩選、排序或作為複雜表達式的一部分時,這非常有用。不選擇未使用的值可以減少資料庫中的冗餘工作,這應該會帶來更好的效能。
例如,如果想找到具有超過 5 個條目的部落格,但對確切的條目數量不感興趣,可以這樣做
>>> from django.db.models import Count
>>> blogs = Blog.objects.alias(entries=Count("entry")).filter(entries__gt=5)
alias()
可以與 annotate()
、exclude()
、filter()
、order_by()
和 update()
結合使用。若要將別名表達式與其他方法(例如 aggregate()
)一起使用,必須將其升級為註解
Blog.objects.alias(entries=Count("entry")).annotate(
entries=F("entries"),
).aggregate(Sum("entries"))
filter()
和 order_by()
可以直接採用表達式,但表達式的建構和使用通常不會在同一個地方進行(例如,QuerySet
方法會建立表達式,以供稍後在視圖中使用)。 alias()
允許逐步建立複雜的表達式,可能會跨越多個方法和模組,透過別名參考表達式部分,並且僅將 annotate()
用於最終結果。
order_by()
¶
- order_by(*fields)¶
預設情況下,QuerySet
返回的結果會按照模型 Meta
中的 ordering
選項所提供的排序元組進行排序。 您可以使用 order_by
方法來針對每個 QuerySet
覆寫此行為。
範例
Entry.objects.filter(pub_date__year=2005).order_by("-pub_date", "headline")
上述結果會先依 pub_date
遞減排序,然後再依 headline
遞增排序。"-pub_date"
前面的負號表示遞減排序。遞增排序是預設的。若要隨機排序,請使用 "?"
,如下所示
Entry.objects.order_by("?")
注意:order_by('?')
查詢可能會很耗費資源且速度緩慢,取決於您使用的資料庫後端。
若要依不同模型中的欄位排序,請使用與跨模型關聯查詢時相同的語法。也就是說,欄位名稱,後面接著雙底線 (__
),接著是新模型中的欄位名稱,依此類推,您想要加入多少模型都可以。例如
Entry.objects.order_by("blog__name", "headline")
如果您嘗試依另一個模型的關聯欄位排序,Django 將會使用相關模型的預設排序,如果沒有指定 Meta.ordering
,則會依相關模型的主鍵排序。例如,由於 Blog
模型沒有指定預設排序
Entry.objects.order_by("blog")
...與此相同
Entry.objects.order_by("blog__id")
如果 Blog
有 ordering = ['name']
,則第一個 queryset 將與此相同
Entry.objects.order_by("blog__name")
您也可以透過呼叫表達式上的 asc()
或 desc()
,依查詢表達式排序
Entry.objects.order_by(Coalesce("summary", "headline").desc())
asc()
和 desc()
具有控制如何排序 Null 值的引數 (nulls_first
和 nulls_last
)。
如果您同時使用 distinct()
,請謹慎依相關模型中的欄位排序。請參閱 distinct()
中的註解,了解相關模型排序如何變更預期結果。
注意
允許指定多值欄位來排序結果(例如,ManyToManyField
欄位,或 ForeignKey
欄位的反向關聯)。
請考慮以下情況
class Event(Model):
parent = models.ForeignKey(
"self",
on_delete=models.CASCADE,
related_name="children",
)
date = models.DateField()
Event.objects.order_by("children__date")
在這裡,每個 Event
可能有多個排序資料;每個具有多個 children
的 Event
將會多次返回到 order_by()
建立的新 QuerySet
中。換句話說,在 QuerySet
上使用 order_by()
可能會返回比您開始處理的項目更多的項目,這可能既非預期也沒用。
因此,使用多值欄位排序結果時請小心。**如果**您可以確定您正在排序的每個項目只有一個排序資料,則此方法應該不會有問題。如果不是,請確保結果符合您的預期。
無法指定排序是否應區分大小寫。關於大小寫靈敏度,Django 將會以資料庫後端通常的排序方式排序結果。
您可以使用 Lower
將欄位轉換為小寫,以實現大小寫一致的排序
Entry.objects.order_by(Lower("headline").desc())
如果您不希望任何排序應用於查詢,甚至包括預設排序,請呼叫不帶任何參數的 order_by()
。
您可以透過檢查 QuerySet.ordered
屬性來判斷查詢是否已排序,如果 QuerySet
已以任何方式排序,則該屬性將為 True
。
每個 order_by()
呼叫都會清除任何先前的排序。例如,此查詢將依 pub_date
排序,而不是依 headline
排序
Entry.objects.order_by("headline").order_by("pub_date")
警告
排序不是免費的操作。您新增到排序的每個欄位都會增加資料庫的成本。您新增的每個外部索引鍵也會隱含地包含其所有預設排序。
如果查詢沒有指定排序,則結果會從資料庫中以未指定的順序返回。只有在依一組可唯一識別結果中每個物件的欄位排序時,才能保證特定的排序。例如,如果 name
欄位不是唯一的,則依此排序無法保證具有相同名稱的物件始終以相同的順序出現。
reverse()
¶
- reverse()¶
使用 reverse()
方法來反轉 queryset 元素的返回順序。第二次呼叫 reverse()
會將排序恢復為正常方向。
若要檢索 queryset 中的「最後」五個項目,您可以執行此操作
my_queryset.reverse()[:5]
請注意,這與在 Python 中從序列末尾切片略有不同。上面的範例會先返回最後一個項目,然後是倒數第二個項目,依此類推。如果我們有一個 Python 序列並查看 seq[-5:]
,我們會先看到倒數第五個項目。Django 不支援這種存取模式(從末尾切片),因為它無法在 SQL 中有效率地執行。
此外,請注意,reverse()
通常應該僅在具有定義排序的 QuerySet
上呼叫(例如,當針對定義預設排序的模型進行查詢時,或使用 order_by()
時)。如果未針對給定的 QuerySet
定義此類排序,則在其上呼叫 reverse()
沒有實際效果(在呼叫 reverse()
之前排序未定義,之後仍將保持未定義)。
distinct()
¶
- distinct(*fields)¶
返回一個新的 QuerySet
,其在 SQL 查詢中使用 SELECT DISTINCT
。這會消除查詢結果中的重複列。
預設情況下,QuerySet
不會消除重複的列。實際上,這很少是問題,因為諸如 Blog.objects.all()
之類的簡單查詢不會引入重複結果列的可能性。但是,如果您的查詢跨越多個表,則在評估 QuerySet
時,可能會得到重複的結果。這時您可以使用 distinct()
。
注意
在 order_by()
呼叫中使用的任何欄位都會包含在 SQL SELECT
資料行中。當與 distinct()
結合使用時,有時可能會導致意外的結果。如果您依相關模型中的欄位排序,則這些欄位將會新增至選取的資料行,並且可能會使原本重複的列看起來是不同的。由於額外的資料行不會出現在傳回的結果中(它們僅存在以支援排序),因此有時看起來會返回非不同的結果。
同樣,如果您使用 values()
查詢來限制選取的資料行,則任何 order_by()
(或預設模型排序)中使用的資料行仍會參與,並可能會影響結果的唯一性。
這裡的重點是,如果您使用 distinct()
,請小心依相關模型排序。同樣,當同時使用 distinct()
和 values()
時,請小心依不在 values()
呼叫中的欄位排序。
僅在 PostgreSQL 上,您可以傳遞位置引數 (*fields
) 來指定 DISTINCT
應用的欄位名稱。這會轉換為 SELECT DISTINCT ON
SQL 查詢。以下是差異。對於普通的 distinct()
呼叫,資料庫在確定哪些列不同時,會比較每一列中的每個欄位。對於具有指定欄位名稱的 distinct()
呼叫,資料庫僅會比較指定的欄位名稱。
注意
當您指定欄位名稱時,您必須在 QuerySet
中提供 order_by()
,且 order_by()
中的欄位必須以 distinct()
中的欄位開始,並且順序相同。
例如,SELECT DISTINCT ON (a)
會為欄位 a
中的每個值提供第一列。如果您沒有指定排序方式,您將會取得任意的列。
範例(第一個之後的範例僅適用於 PostgreSQL)
>>> Author.objects.distinct()
[...]
>>> Entry.objects.order_by("pub_date").distinct("pub_date")
[...]
>>> Entry.objects.order_by("blog").distinct("blog")
[...]
>>> Entry.objects.order_by("author", "pub_date").distinct("author", "pub_date")
[...]
>>> Entry.objects.order_by("blog__name", "mod_date").distinct("blog__name", "mod_date")
[...]
>>> Entry.objects.order_by("author", "pub_date").distinct("author")
[...]
注意
請記住,order_by()
會使用任何已定義的預設相關模型排序。您可能必須明確地依關聯的 _id
或參照欄位排序,以確保 DISTINCT ON
表達式與 ORDER BY
子句開頭的表達式相符。例如,如果 Blog
模型定義了依 name
的 ordering
Entry.objects.order_by("blog").distinct("blog")
…將無法運作,因為查詢將依 blog__name
排序,因此與 DISTINCT ON
表達式不符。您必須明確地依關聯的 _id
欄位(在此情況下為 blog_id
)或參照的欄位(blog__pk
)排序,以確保兩個表達式都相符。
values()
¶
- values(*fields, **expressions)¶
返回一個 QuerySet
,當作為可迭代物件使用時,該物件會返回字典而非模型實例。
每個字典都代表一個物件,其鍵對應於模型物件的屬性名稱。
此範例比較了 values()
的字典與一般的模型物件
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith="Beatles")
<QuerySet [<Blog: Beatles Blog>]>
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith="Beatles").values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
values()
方法接受可選的位置參數,*fields
,用於指定應限制 SELECT
的欄位名稱。如果您指定了欄位,則每個字典將僅包含您指定的欄位的欄位鍵/值。如果您沒有指定欄位,則每個字典將包含資料庫表格中每個欄位的鍵和值。
範例
>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values("id", "name")
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
values()
方法還接受可選的關鍵字引數,**expressions
,這些引數會傳遞到 annotate()
>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower("name"))
<QuerySet [{'lower_name': 'beatles blog'}]>
您可以在排序中使用內建和 自訂查詢。例如
>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values("name__lower")
<QuerySet [{'name__lower': 'beatles blog'}]>
在 values()
子句中的聚合會在同一個 values()
子句中的其他參數之前應用。如果您需要依另一個值分組,請將其添加到較早的 values()
子句中。例如
>>> from django.db.models import Count
>>> Blog.objects.values("entry__authors", entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values("entry__authors").annotate(entries=Count("entry"))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>
一些值得一提的細微之處
如果您有一個名為
foo
的欄位,該欄位是一個ForeignKey
,則預設的values()
呼叫將返回一個名為foo_id
的字典鍵,因為這是儲存實際值的隱藏模型屬性的名稱(foo
屬性參照相關的模型)。當您呼叫values()
並傳入欄位名稱時,您可以傳入foo
或foo_id
,您將會得到相同的結果(字典鍵將與您傳入的欄位名稱相符)。例如
>>> Entry.objects.values() <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]> >>> Entry.objects.values("blog") <QuerySet [{'blog': 1}, ...]> >>> Entry.objects.values("blog_id") <QuerySet [{'blog_id': 1}, ...]>
當將
values()
與distinct()
一起使用時,請注意排序可能會影響結果。請參閱distinct()
中的註解以了解詳細資訊。如果您在
extra()
呼叫之後使用values()
子句,則extra()
中的select
參數所定義的任何欄位都必須明確包含在values()
呼叫中。在values()
呼叫之後進行的任何extra()
呼叫都將忽略其額外選取的欄位。結合轉換和聚合需要使用兩個
annotate()
呼叫,可以是明確的或是作為values()
的關鍵字引數。如上所述,如果轉換已在相關欄位類型上註冊,則可以省略第一個annotate()
,因此以下範例是等效的>>> from django.db.models import CharField, Count >>> from django.db.models.functions import Lower >>> CharField.register_lookup(Lower) >>> Blog.objects.values("entry__authors__name__lower").annotate(entries=Count("entry")) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.values(entry__authors__name__lower=Lower("entry__authors__name")).annotate( ... entries=Count("entry") ... ) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]> >>> Blog.objects.annotate(entry__authors__name__lower=Lower("entry__authors__name")).values( ... "entry__authors__name__lower" ... ).annotate(entries=Count("entry")) <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
當您知道您只需要少量可用欄位的值,並且不需要模型實例物件的功能時,此方法很有用。僅選取您需要使用的欄位會更有效率。
最後,請注意,您可以在 values()
呼叫之後呼叫 filter()
、order_by()
等,這表示這兩個呼叫是相同的
Blog.objects.values().order_by("id")
Blog.objects.order_by("id").values()
建立 Django 的人們偏好先放置所有影響 SQL 的方法,然後(可選地)放置任何影響輸出的方法(例如 values()
),但這其實並不重要。這是您展現個性的機會。
您也可以透過 OneToOneField
、ForeignKey
和 ManyToManyField
屬性參照相關模型上的欄位,這些模型具有反向關係
>>> Blog.objects.values("name", "entry__headline")
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
{'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>
警告
因為 ManyToManyField
屬性和反向關係可以有多個相關的列,因此包含這些可能會對您的結果集的大小產生乘數效應。如果您在 values()
查詢中包含多個此類欄位,這種情況將尤其明顯,在這種情況下,將會返回所有可能的組合。
SQLite 上 JSONField
的特殊值
由於在 SQLite 上實作 JSON_EXTRACT
和 JSON_TYPE
SQL 函數的方式,以及缺少 BOOLEAN
資料類型,values()
將會返回 True
、False
和 None
,而不是 JSONField
鍵轉換的 "true"
、"false"
和 "null"
字串。
values_list()
¶
- values_list(*fields, flat=False, named=False)¶
這與 values()
相似,不同之處在於,它在迭代時會返回元組,而不是返回字典。每個元組都包含從傳入 values_list()
呼叫的個別欄位或表達式中的值 — 因此第一個項目是第一個欄位,依此類推。例如
>>> Entry.objects.values_list("id", "headline")
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list("id", Lower("headline"))
<QuerySet [(1, 'first entry'), ...]>
如果您只傳入單個欄位,您還可以傳入 flat
參數。如果 True
,則表示返回的結果是單個值,而不是 1 元組。範例應該會使差異更清楚
>>> Entry.objects.values_list("id").order_by("id")
<QuerySet[(1,), (2,), (3,), ...]>
>>> Entry.objects.values_list("id", flat=True).order_by("id")
<QuerySet [1, 2, 3, ...]>
當有多個欄位時傳入 flat
會產生錯誤。
您可以傳入 named=True
以將結果作為 namedtuple()
取得
>>> Entry.objects.values_list("id", "headline", named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>
使用具名元組 (named tuple) 可能會讓結果更易讀,但會因為將結果轉換為具名元組而產生些微的效能損失。
如果沒有傳遞任何值給 values_list()
,它將會依照模型宣告的順序回傳所有欄位。
一個常見的需求是取得特定模型實例的特定欄位值。要達成此目的,可以使用 values_list()
接著呼叫 get()
。
>>> Entry.objects.values_list("headline", flat=True).get(pk=1)
'First entry'
values()
和 values_list()
都是為了特定使用情境而設計的優化:在不需建立模型實例的負擔下,取得資料的子集。當處理多對多關係和其他多值關係(例如反向外鍵的一對多關係)時,這個隱喻就會失效,因為「一列對應一個物件」的假設不成立。
例如,請注意在跨 ManyToManyField
查詢時的行為
>>> Author.objects.values_list("name", "entry__headline")
<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
('George Orwell', 'Why Socialists Do Not Believe in Fun'),
('George Orwell', 'In Defence of English Cooking'),
('Don Quixote', None)]>
有多個條目的作者會出現多次,而沒有任何條目的作者的條目標題則會是 None
。
類似地,當查詢反向外鍵時,沒有任何作者的條目會顯示 None
。
>>> Entry.objects.values_list("authors")
<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>
SQLite 上 JSONField
的特殊值
由於 SQLite 上 JSON_EXTRACT
和 JSON_TYPE
SQL 函數的實作方式,以及缺乏 BOOLEAN
資料類型,values_list()
將會針對 JSONField
的鍵轉換回傳 True
、False
和 None
,而不是 "true"
、"false"
和 "null"
字串。
dates()
¶
- dates(field, kind, order='ASC')¶
回傳一個 QuerySet
,它會評估為 datetime.date
物件的列表,表示 QuerySet
內容中特定類型的所有可用日期。
field
應該是您模型的 DateField
的名稱。kind
應該是 "year"
、"month"
、"week"
或 "day"
其中之一。結果列表中的每個 datetime.date
物件都會被「截斷」為給定的 type
。
"year"
回傳該欄位所有相異的年份值的列表。"month"
回傳該欄位所有相異的年/月值的列表。"week"
回傳該欄位所有相異的年/週值的列表。所有日期都會是星期一。"day"
回傳該欄位所有相異的年/月/日值的列表。
order
,預設為 'ASC'
,應該是 'ASC'
或 'DESC'
。這指定如何排序結果。
範例
>>> Entry.objects.dates("pub_date", "year")
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates("pub_date", "month")
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates("pub_date", "week")
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates("pub_date", "day")
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates("pub_date", "day", order="DESC")
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains="Lennon").dates("pub_date", "day")
[datetime.date(2005, 3, 20)]
datetimes()
¶
- datetimes(field_name, kind, order='ASC', tzinfo=None)¶
回傳一個 QuerySet
,它會評估為 datetime.datetime
物件的列表,表示 QuerySet
內容中特定類型的所有可用日期。
field_name
應該是您模型的 DateTimeField
的名稱。
kind
應該是 "year"
、"month"
、"week"
、"day"
、"hour"
、"minute"
或 "second"
其中之一。結果列表中的每個 datetime.datetime
物件都會被「截斷」為給定的 type
。
order
,預設為 'ASC'
,應該是 'ASC'
或 'DESC'
。這指定如何排序結果。
tzinfo
定義在截斷之前將日期時間轉換成的時區。實際上,給定的日期時間會根據使用的時區而有不同的表示。此參數必須是 datetime.tzinfo
物件。如果它是 None
,Django 會使用目前時區。當 USE_TZ
為 False
時,它沒有任何作用。
注意
此函數直接在資料庫中執行時區轉換。因此,您的資料庫必須能夠解釋 tzinfo.tzname(None)
的值。這轉換成以下要求
SQLite:沒有要求。轉換會在 Python 中執行。
PostgreSQL:沒有要求(請參閱時區)。
Oracle:沒有要求(請參閱選擇時區檔案)。
MySQL:使用mysql_tzinfo_to_sql載入時區表。
none()
¶
- none()¶
呼叫 none()
將會建立一個永遠不會回傳任何物件的 queryset,並且在存取結果時不會執行任何查詢。qs.none()
queryset 是 EmptyQuerySet
的實例。
範例
>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True
all()
¶
- all()¶
回傳目前 QuerySet
(或 QuerySet
子類別)的副本。這在您可能想要傳入模型管理員或 QuerySet
並對結果執行進一步篩選的情況下很有用。在任一物件上呼叫 all()
之後,您肯定會有一個 QuerySet
可以使用。
當 QuerySet
被評估時,它通常會快取其結果。如果資料庫中的資料可能在 QuerySet
被評估之後發生變更,您可以藉由在先前評估過的 QuerySet
上呼叫 all()
來取得相同查詢的更新結果。
union()
¶
- union(*other_qs, all=False)¶
使用 SQL 的 UNION
運算子來合併兩個或多個 QuerySet
的結果。例如
>>> qs1.union(qs2, qs3)
UNION
運算子預設只會選取相異的值。若要允許重複的值,請使用 all=True
引數。
union()
、intersection()
和 difference()
會回傳第一個 QuerySet
類型的模型實例,即使引數是其他模型的 QuerySet
也是如此。只要所有 QuerySet
中的 SELECT
清單相同(至少類型相同,名稱不重要,只要類型順序相同),傳遞不同的模型就可以運作。在這種情況下,您必須在套用至結果 QuerySet
的 QuerySet
方法中使用第一個 QuerySet
的欄位名稱。例如:
>>> qs1 = Author.objects.values_list("name")
>>> qs2 = Entry.objects.values_list("headline")
>>> qs1.union(qs2).order_by("name")
此外,只允許在結果 QuerySet
上使用 LIMIT
、OFFSET
、COUNT(*)
、ORDER BY
,以及指定欄位(即切片、count()
、exists()
、order_by()
和 values()
/values_list()
)。此外,資料庫對組合查詢中允許的操作有所限制。例如,大多數資料庫不允許在組合查詢中使用 LIMIT
或 OFFSET
。
intersection()
¶
- intersection(*other_qs)¶
使用 SQL 的 INTERSECT
運算子來回傳兩個或多個 QuerySet
的共用元素。例如:
>>> qs1.intersection(qs2, qs3)
有關一些限制,請參閱 union()
。
difference()
¶
- difference(*other_qs)¶
使用 SQL 的 EXCEPT
運算子,只保留存在於 QuerySet
中,但不存在於其他 QuerySet
中的元素。例如:
>>> qs1.difference(qs2, qs3)
有關一些限制,請參閱 union()
。
extra()
¶
- extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)¶
有時,Django 查詢語法本身無法輕易表達複雜的 WHERE
子句。對於這些極端情況,Django 提供了 extra()
QuerySet
修飾符,這是一個將特定子句注入 QuerySet
所產生 SQL 中的鉤子。
請將此方法作為最後手段使用
這是一個舊的 API,我們打算在未來某個時候將其棄用。只有在您無法使用其他 queryset 方法表達您的查詢時才使用它。如果您確實需要使用它,請使用 提交工單,並使用 QuerySet.extra 關鍵字 描述您的使用案例(請先查看現有工單列表),以便我們可以增強 QuerySet API 以允許移除 extra()
。我們不再改進或修復此方法的錯誤。
例如,以下 extra()
的用法
>>> qs.extra(
... select={"val": "select col from sometable where othercol = %s"},
... select_params=(someparam,),
... )
等同於
>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))
使用 RawSQL
的主要好處是,您可以在需要時設定 output_field
。主要缺點是,如果您在原始 SQL 中引用 queryset 的某些表格別名,則 Django 可能會變更該別名(例如,當 queryset 在另一個查詢中用作子查詢時)。
警告
每當您使用 extra()
時,您都應該非常小心。每次使用時,您都應該使用 params
來跳脫任何使用者可以控制的參數,以防止 SQL 注入攻擊。
您也不得在 SQL 字串中加上預留位置的引號。此範例由於 %s
周圍的引號而容易遭受 SQL 注入攻擊
SELECT col FROM sometable WHERE othercol = '%s' # unsafe!
您可以閱讀更多關於 Django 的 SQL 注入保護 如何運作的資訊。
根據定義,這些額外的查詢可能無法移植到不同的資料庫引擎(因為您正在明確編寫 SQL 程式碼)並違反了 DRY 原則,因此您應盡可能避免使用它們。
指定 params
、select
、where
或 tables
中的一項或多項。這些參數都不是必要的,但您至少應使用其中一個。
select
select
參數可讓您在SELECT
子句中放入額外的欄位。它應該是一個字典,將屬性名稱對應到用於計算該屬性的 SQL 子句。範例
Entry.objects.extra(select={"is_recent": "pub_date > '2006-01-01'"})
因此,每個
Entry
物件都會有一個額外的屬性is_recent
,這是一個布林值,表示項目的pub_date
是否大於 2006 年 1 月 1 日。Django 會將指定的 SQL 片段直接插入
SELECT
陳述式中,因此上述範例產生的 SQL 會類似於SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent FROM blog_entry;
下一個範例更進階;它會執行子查詢,為每個產生的
Blog
物件提供一個entry_count
屬性,這是一個相關Entry
物件的整數計數Blog.objects.extra( select={ "entry_count": "SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id" }, )
在這個特定案例中,我們利用了查詢的
FROM
子句中已經包含blog_blog
表格這一事實。上述範例產生的 SQL 會是
SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count FROM blog_blog;
請注意,大多數資料庫引擎在子查詢周圍所需的括號在 Django 的
select
子句中不是必要的。在某些罕見情況下,您可能希望將參數傳遞到
extra(select=...)
中的 SQL 片段。為此,請使用select_params
參數。例如,這會起作用
Blog.objects.extra( select={"a": "%s", "b": "%s"}, select_params=("one", "two"), )
如果您需要在選取字串中使用文字
%s
,請使用序列%%s
。where
/tables
您可以使用
where
定義明確的 SQLWHERE
子句(可能是為了執行非明確的聯結)。您可以使用tables
手動將表格新增至 SQLFROM
子句。where
和tables
都採用字串列表。所有where
參數都會與任何其他搜尋條件「AND」在一起。範例
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
...大致轉換為以下 SQL
SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')
如果您指定查詢中已經使用的表格,請小心使用
tables
參數。當您透過tables
參數新增額外表格時,Django 會假設您想要額外包含該表格一次(如果它已經被包含)。這會產生問題,因為表格名稱會被賦予別名。如果一個表格在 SQL 陳述式中出現多次,則第二次和後續出現的表格必須使用別名,以便資料庫可以區分它們。如果您在額外的where
參數中引用您新增的額外表格,這將導致錯誤。通常,您只會新增查詢中尚未出現的額外表格。但是,如果發生上述情況,則有幾種解決方案。首先,看看是否可以在不包含額外表格的情況下進行,並使用查詢中已有的表格。如果無法做到這一點,請將您的
extra()
呼叫放在 queryset 建構的最前面,以便您的表格是第一個使用的表格。最後,如果所有方法都失敗了,請查看產生的查詢,並重寫您的where
新增內容,以使用賦予您額外表格的別名。每次您以相同方式建構 queryset 時,別名都會相同,因此您可以依賴別名名稱而不變更。order_by
如果您需要使用您透過
extra()
包含的一些新欄位或表格來排序產生的 queryset,請使用extra()
的order_by
參數,並傳入字串序列。這些字串應該是模型欄位(如 queryset 上正常的order_by()
方法),格式為table_name.column_name
,或您在extra()
的select
參數中指定的欄位別名。例如
q = Entry.objects.extra(select={"is_recent": "pub_date > '2006-01-01'"}) q = q.extra(order_by=["-is_recent"])
這會將
is_recent
為 true 的所有項目排序到結果集的前面(在降序排序中,True
會排在False
之前)。順帶一提,這表示您可以多次呼叫
extra()
,並且它的行為會如您預期(每次都新增新的限制)。params
上述描述的
where
參數可能會使用標準 Python 資料庫字串預留位置 —'%s'
表示資料庫引擎應自動加上引號的參數。params
參數是要替換的任何額外參數的列表。範例
Entry.objects.extra(where=["headline=%s"], params=["Lennon"])
請始終使用
params
而不是將值直接嵌入where
中,因為params
會確保值根據您的特定後端正確加上引號。例如,引號會被正確地跳脫。不良範例
Entry.objects.extra(where=["headline='Lennon'"])
良好範例
Entry.objects.extra(where=["headline=%s"], params=["Lennon"])
警告
如果您正在 MySQL 上執行查詢,請注意,當混合類型時,MySQL 的靜默類型強制轉換可能會導致意外的結果。如果您查詢字串類型欄位,但使用整數值,MySQL 會在執行比較之前將表格中所有值的類型強制轉換為整數。例如,如果您的表格包含值 'abc'
、'def'
,並且您查詢 WHERE mycolumn=0
,則兩列都會匹配。為避免這種情況,請在查詢中使用值之前執行正確的類型轉換。
defer()
¶
- defer(*fields)¶
在某些複雜的資料模型情境中,您的模型可能包含許多欄位,其中一些欄位可能包含大量資料(例如,文字欄位),或需要大量的處理才能將其轉換為 Python 物件。如果您在某些情況下使用 queryset 的結果,而您不確定在最初獲取資料時是否需要這些特定欄位,您可以告知 Django 不要從資料庫中檢索它們。
這是透過將要延遲載入的欄位名稱傳遞給 defer()
來完成的。
Entry.objects.defer("headline", "body")
具有延遲載入欄位的 queryset 仍將回傳模型實例。如果您存取該欄位(一次一個,而不是一次全部延遲載入的欄位),則每個延遲載入的欄位都會從資料庫中檢索。
注意
延遲載入的欄位不會像這樣從非同步程式碼進行惰性載入。相反地,您會收到一個 SynchronousOnlyOperation
例外。如果您正在編寫非同步程式碼,則不應嘗試存取任何您 defer()
的欄位。
您可以多次呼叫 defer()
。每次呼叫都會將新欄位添加到延遲載入的集合中。
# Defers both the body and headline fields.
Entry.objects.defer("body").filter(rating=5).defer("headline")
將欄位添加到延遲載入集合的順序並不重要。使用已延遲載入的欄位名稱呼叫 defer()
是無害的(該欄位仍將被延遲載入)。
您可以使用標準的雙底線表示法來分隔相關欄位,以延遲載入相關模型中的欄位(如果相關模型是透過 select_related()
載入)。
Blog.objects.select_related().defer("entry__headline", "entry__body")
如果您想清除延遲載入的欄位集合,請將 None
作為參數傳遞給 defer()
。
# Load all fields immediately.
my_queryset.defer(None)
即使您要求延遲載入,模型中的某些欄位也不會被延遲載入。您永遠無法延遲載入主鍵。如果您正在使用 select_related()
來檢索相關模型,則不應延遲載入從主模型連線到相關模型的欄位,否則會導致錯誤。
同樣地,呼叫 defer()
(或其對應的 only()
)包含聚合中的引數(例如,使用 annotate()
的結果)是沒有意義的:這樣做會引發例外。聚合的值將始終被提取到結果的 queryset 中。
注意
defer()
方法(及其近親 only()
,如下所述)僅適用於進階使用案例。當您仔細分析查詢並瞭解確切需要哪些資訊,並已測量過回傳您需要的欄位與回傳模型完整欄位之間的差異將會很大時,它們提供了一種優化方法。
即使您認為自己處於進階使用案例的情境中,**只有在您無法在 queryset 載入時判斷是否需要額外的欄位時才使用** defer()
。如果您經常載入和使用特定的資料子集,那麼您可以做出的最佳選擇是正規化您的模型,並將未載入的資料放入單獨的模型(和資料庫表格)。如果欄位由於某種原因必須保留在同一個表格中,請使用 Meta.managed = False
(請參閱 managed 屬性
文件)建立一個僅包含您通常需要載入的欄位的模型,並在您可能呼叫 defer()
的地方使用它。這會讓您的程式碼對讀者更明確,速度會稍微快一點,並在 Python 程序中消耗更少的記憶體。
例如,這兩個模型都使用相同的底層資料庫表格。
class CommonlyUsedModel(models.Model):
f1 = models.CharField(max_length=10)
class Meta:
managed = False
db_table = "app_largetable"
class ManagedModel(models.Model):
f1 = models.CharField(max_length=10)
f2 = models.CharField(max_length=10)
class Meta:
db_table = "app_largetable"
# Two equivalent QuerySets:
CommonlyUsedModel.objects.all()
ManagedModel.objects.defer("f2")
如果未管理的模型中需要複製許多欄位,則最好建立一個具有共享欄位的抽象模型,然後讓未管理的模型和管理的模型都繼承自該抽象模型。
only()
¶
- only(*fields)¶
only()
方法本質上與 defer()
相反。只有傳遞到此方法且未指定為延遲載入的欄位,會在評估 queryset 時立即載入。
如果您有一個模型,其中幾乎所有欄位都需要延遲載入,則使用 only()
指定補充的欄位集合可以使程式碼更簡單。
假設您有一個模型,其中包含欄位 name
、age
和 biography
。以下兩個 queryset 在延遲載入的欄位方面是相同的。
Person.objects.defer("age", "biography")
Person.objects.only("name")
當您呼叫 only()
時,它會取代要立即載入的欄位集合。此方法的名稱是助記符:只有這些欄位會立即載入;其餘的則會延遲載入。因此,連續呼叫 only()
只會考慮最後的欄位。
# This will defer all fields except the headline.
Entry.objects.only("body", "rating").only("headline")
由於 defer()
的作用是累加的(將欄位新增到延遲載入清單),因此您可以結合呼叫 only()
和 defer()
,而事情將會按照邏輯運作。
# Final result is that everything except "headline" is deferred.
Entry.objects.only("headline", "body").defer("body")
# Final result loads headline immediately.
Entry.objects.defer("body").only("headline", "body")
在 defer()
文件中注意事項中的所有警告也適用於 only()
。請謹慎使用它,並僅在用盡其他選項後才使用。
使用 only()
並省略使用 select_related()
請求的欄位也是錯誤的。另一方面,呼叫不帶任何引數的 only()
將會回傳 queryset 提取的每個欄位(包括註釋)。
與 defer()
一樣,您無法從非同步程式碼存取未載入的欄位並期望它們載入。相反地,您會收到一個 SynchronousOnlyOperation
例外。請確保您可能存取的所有欄位都在您的 only()
呼叫中。
using()
¶
- using(alias)¶
如果您使用多個資料庫,此方法用於控制 QuerySet
將針對哪個資料庫進行評估。此方法唯一接受的引數是資料庫的別名,如 DATABASES
中所定義。
例如
# queries the database with the 'default' alias.
>>> Entry.objects.all()
# queries the database with the 'backup' alias
>>> Entry.objects.using("backup")
select_for_update()
¶
- select_for_update(nowait=False, skip_locked=False, of=(), no_key=False)¶
返回一個查詢集(queryset),它會在事務結束前鎖定資料列,並在支援的資料庫上產生一個 SELECT ... FOR UPDATE
SQL 語句。
例如
from django.db import transaction
entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
for entry in entries:
...
當查詢集被評估時(在此例中為 for entry in entries
),所有匹配的條目將被鎖定直到事務區塊結束,這表示其他事務將無法變更或取得它們的鎖。
通常,如果另一個事務已經取得其中一個選定資料列的鎖,則查詢將會被阻塞,直到鎖被釋放。如果這不是您想要的行為,請呼叫 select_for_update(nowait=True)
。這會使呼叫變成非阻塞。如果衝突的鎖已被另一個事務取得,則在評估查詢集時會引發 DatabaseError
。您也可以使用 select_for_update(skip_locked=True)
來忽略被鎖定的資料列。nowait
和 skip_locked
是互斥的,嘗試同時啟用這兩個選項來呼叫 select_for_update()
將會導致 ValueError
。
預設情況下,select_for_update()
會鎖定查詢所選取的所有資料列。例如,除了查詢集模型的資料列之外,在 select_related()
中指定的相關物件的資料列也會被鎖定。如果不希望這樣,請使用與 select_related()
相同的欄位語法,在 select_for_update(of=(...))
中指定您想要鎖定的相關物件。使用值 'self'
來參照查詢集的模型。
在 select_for_update(of=(...))
中鎖定父模型
如果您想在使用 多表繼承 時鎖定父模型,您必須在 of
引數中指定父連結欄位(預設為 <parent_model_name>_ptr
)。例如
Restaurant.objects.select_for_update(of=("self", "place_ptr"))
使用 select_for_update(of=(...))
搭配指定的欄位
如果您想鎖定模型並指定選定的欄位,例如使用 values()
,您必須在 of
引數中從每個模型中至少選取一個欄位。未選取欄位的模型將不會被鎖定。
僅在 PostgreSQL 上,您可以傳遞 no_key=True
以取得較弱的鎖定,這仍然允許在鎖定生效時建立僅參考鎖定資料列的資料列(例如,透過外鍵)。PostgreSQL 文件中有更多關於資料列層級鎖定模式的詳細資訊。
您無法在可為空的關係上使用 select_for_update()
>>> Person.objects.select_related("hometown").select_for_update()
Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join
為了避免此限制,如果您不在意空物件,您可以排除它們
>>> Person.objects.select_related("hometown").select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>
postgresql
、oracle
和 mysql
資料庫後端支援 select_for_update()
。但是,MariaDB 僅支援 nowait
引數,MariaDB 10.6+ 也支援 skip_locked
引數,而 MySQL 支援 nowait
、skip_locked
和 of
引數。no_key
引數僅在 PostgreSQL 上支援。
將 nowait=True
、skip_locked=True
、no_key=True
或 of
傳遞給 select_for_update()
,使用不支援這些選項的資料庫後端(例如 MySQL)會引發 NotSupportedError
。這可防止程式碼意外地被阻塞。
在支援 SELECT ... FOR UPDATE
的後端上,在自動提交模式下評估具有 select_for_update()
的查詢集會發生 TransactionManagementError
錯誤,因為在這種情況下資料列不會被鎖定。如果允許,這將導致資料損毀,並且很容易因呼叫預期在事務之外運行的程式碼而導致。
在不支援 SELECT ... FOR UPDATE
的後端(例如 SQLite)上使用 select_for_update()
將不會有任何作用。SELECT ... FOR UPDATE
不會被新增到查詢中,並且如果在自動提交模式下使用 select_for_update()
也不會引發錯誤。
警告
儘管 select_for_update()
通常在自動提交模式下失敗,但由於 TestCase
自動將每個測試包裝在事務中,因此即使在 atomic()
區塊之外的 TestCase
中呼叫 select_for_update()
也會(可能出乎意料地)通過而不會引發 TransactionManagementError
。為了正確測試 select_for_update()
,您應該使用 TransactionTestCase
。
某些表達式可能不被支援
PostgreSQL 不支援帶有 Window
表達式的 select_for_update()
。
raw()
¶
- raw(raw_query, params=(), translations=None, using=None)¶
採用原始 SQL 查詢,執行它並返回一個 django.db.models.query.RawQuerySet
實例。這個 RawQuerySet
實例可以像普通的 QuerySet
一樣被迭代以提供物件實例。
有關更多資訊,請參閱執行原始 SQL 查詢。
警告
raw()
總是會觸發新的查詢,並且不會考慮先前的篩選。因此,它通常應該從 Manager
或從新的 QuerySet
實例中呼叫。
返回新 QuerySet
的運算符¶
合併的查詢集必須使用相同的模型。
AND (&
)¶
結合兩個 QuerySet
,使用 SQL 的 AND
運算子,其方式類似於串聯篩選器。
以下是等效的
Model.objects.filter(x=1) & Model.objects.filter(y=2)
Model.objects.filter(x=1).filter(y=2)
SQL 等效語法
SELECT ... WHERE x=1 AND y=2
OR (|
)¶
結合兩個 QuerySet
,使用 SQL 的 OR
運算子。
以下是等效的
Model.objects.filter(x=1) | Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))
SQL 等效語法
SELECT ... WHERE x=1 OR y=2
|
不是可交換的運算,因為可能會產生不同(但等效)的查詢。
XOR (^
)¶
結合兩個 QuerySet
,使用 SQL 的 XOR
運算子。XOR
表達式匹配由奇數個運算元匹配的行。
以下是等效的
Model.objects.filter(x=1) ^ Model.objects.filter(y=2)
from django.db.models import Q
Model.objects.filter(Q(x=1) ^ Q(y=2))
SQL 等效語法
SELECT ... WHERE x=1 XOR y=2
注意
XOR
在 MariaDB 和 MySQL 上原生支援。在其他資料庫上,x ^ y ^ ... ^ z
會被轉換為等效的
(x OR y OR ... OR z) AND
1=MOD(
(CASE WHEN x THEN 1 ELSE 0 END) +
(CASE WHEN y THEN 1 ELSE 0 END) +
...
(CASE WHEN z THEN 1 ELSE 0 END),
2
)
在較舊的版本中,在沒有原生支援 SQL XOR
運算子的資料庫上,XOR
返回由恰好一個運算元匹配的行。先前的行為與 MySQL、MariaDB 和 Python 行為不一致。
不返回 QuerySet
的方法¶
以下 QuerySet
方法會評估 QuerySet
並返回不是 QuerySet
的其他內容。
這些方法不使用快取(請參閱快取和 QuerySet)。相反,它們每次被調用時都會查詢資料庫。
因為這些方法會評估 QuerySet,所以它們是阻塞調用,因此它們的主要(同步)版本不能從異步程式碼中調用。因此,每個方法都有一個帶有 a
前綴的相應異步版本 - 例如,您可以 await aget(…)
,而不是 get(…)
。
除了它們的異步性質外,行為通常沒有差異,但任何差異都會在每個方法旁邊註明。
get()
¶
- get(*args, **kwargs)¶
- aget(*args, **kwargs)¶
異步版本: aget()
返回符合給定查找參數的物件,其格式應如欄位查找中所述。您應該使用保證唯一的查找,例如主鍵或唯一約束中的欄位。例如
Entry.objects.get(id=1)
Entry.objects.get(Q(blog=blog) & Q(entry_number=1))
如果您預期查詢集已經返回一行,您可以使用不帶任何參數的 get()
來返回該行的物件
Entry.objects.filter(pk=1).get()
如果 get()
沒有找到任何物件,它會引發 Model.DoesNotExist
例外
Entry.objects.get(id=-999) # raises Entry.DoesNotExist
如果 get()
找到多個物件,它會引發 Model.MultipleObjectsReturned
例外
Entry.objects.get(name="A Duplicated Name") # raises Entry.MultipleObjectsReturned
這兩個例外類別都是模型類別的屬性,並且特定於該模型。如果您想處理來自不同模型的多次 get()
調用的此類例外,可以使用它們的通用基底類別。例如,您可以使用 django.core.exceptions.ObjectDoesNotExist
來處理來自多個模型的 DoesNotExist
例外
from django.core.exceptions import ObjectDoesNotExist
try:
blog = Blog.objects.get(id=1)
entry = Entry.objects.get(blog=blog, entry_number=1)
except ObjectDoesNotExist:
print("Either the blog or entry doesn't exist.")
create()
¶
- create(**kwargs)¶
- acreate(**kwargs)¶
異步版本: acreate()
一種方便的方法,可以一步完成物件的建立和儲存。因此
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
和
p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
是等效的。
force_insert 參數在其他地方有記載,但它所表示的只是始終會建立新物件。通常您無需擔心這一點。但是,如果您的模型包含您設定的手動主鍵值,並且該值已存在於資料庫中,則呼叫 create()
將會失敗並出現 IntegrityError
,因為主鍵必須是唯一的。如果您使用的是手動主鍵,請準備好處理例外。
get_or_create()
¶
- get_or_create(defaults=None, **kwargs)¶
- aget_or_create(defaults=None, **kwargs)¶
異步版本: aget_or_create()
一種方便的方法,用於查找具有給定 kwargs
的物件(如果您的模型對所有欄位都有預設值,則可能為空),並在必要時建立一個。
返回一個 (object, created)
的元組,其中 object
是檢索或建立的物件,而 created
是一個布林值,用於指定是否建立了新物件。
這旨在防止在並行發出請求時建立重複的物件,並且作為樣板程式碼的捷徑。例如
try:
obj = Person.objects.get(first_name="John", last_name="Lennon")
except Person.DoesNotExist:
obj = Person(first_name="John", last_name="Lennon", birthday=date(1940, 10, 9))
obj.save()
在這裡,對於併發請求,可能會多次嘗試使用相同的參數儲存 Person
。為了避免這種競爭條件,可以使用 get_or_create()
重寫上面的範例,如下所示
obj, created = Person.objects.get_or_create(
first_name="John",
last_name="Lennon",
defaults={"birthday": date(1940, 10, 9)},
)
傳遞給 get_or_create()
的任何關鍵字引數 — 除了一個名為 defaults
的可選引數 — 將在 get()
呼叫中使用。如果找到物件,get_or_create()
會返回該物件和 False
的元組。
警告
此方法是原子操作,前提是資料庫強制執行關鍵字引數的唯一性(請參閱 unique
或 unique_together
)。如果關鍵字引數中使用的欄位沒有唯一性約束,則同時呼叫此方法可能會導致插入具有相同參數的多個列。
您可以透過鏈式使用 filter()
和使用 Q 物件
,為檢索的物件指定更複雜的條件。例如,要檢索 Robert 或 Bob Marley(如果存在),否則建立後者
from django.db.models import Q
obj, created = Person.objects.filter(
Q(first_name="Bob") | Q(first_name="Robert"),
).get_or_create(last_name="Marley", defaults={"first_name": "Bob"})
如果找到多個物件,get_or_create()
會引發 MultipleObjectsReturned
。如果未找到物件,get_or_create()
會實例化並儲存一個新物件,返回新物件和 True
的元組。將根據以下演算法大致建立新物件
params = {k: v for k, v in kwargs.items() if "__" not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()
換句話說,這表示從任何不包含雙底線(表示非精確查找)的非 'defaults'
關鍵字引數開始。然後加入 defaults
的內容,在必要時覆蓋任何索引鍵,並將結果用作模型類別的關鍵字引數。如果 defaults
中有任何可調用物件,則評估它們。如上所述,這是一個簡化的演算法,其中包含了所有相關詳細資訊。內部實作比這有更多的錯誤檢查,並處理一些額外的邊緣條件;如果您有興趣,請閱讀程式碼。
如果您有名為 defaults
的欄位,並且想要在 get_or_create()
中將其用作精確查找,請使用 'defaults__exact'
,如下所示
Foo.objects.get_or_create(defaults__exact="bar", defaults={"defaults": "baz"})
當您使用手動指定的 Primary Key 時,get_or_create()
方法的錯誤行為與 create()
類似。如果需要建立物件,而該鍵已存在於資料庫中,則會引發 IntegrityError
。
最後,關於在 Django 視圖中使用 get_or_create()
的一點說明。請務必僅在 POST
請求中使用它,除非您有充分的理由不這樣做。GET
請求不應對資料產生任何影響。相反地,當頁面的請求對您的資料產生副作用時,請使用 POST
。如需更多資訊,請參閱 HTTP 規範中的 安全方法。
警告
您可以透過 ManyToManyField
屬性和反向關係使用 get_or_create()
。在這種情況下,您將限制該關係內容內的查詢。如果您不一致地使用它,可能會導致一些完整性問題。
假設有以下模型
class Chapter(models.Model):
title = models.CharField(max_length=255, unique=True)
class Book(models.Model):
title = models.CharField(max_length=256)
chapters = models.ManyToManyField(Chapter)
您可以透過 Book 的 chapters 欄位使用 get_or_create()
,但它只會在該書籍的內容中進行提取
>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError
發生這種情況的原因是,它試圖透過書籍「Ulysses」取得或建立「Chapter 1」,但兩者都無法完成:該關係無法提取該章節,因為它與該書籍沒有關聯,但也無法建立它,因為 title
欄位應為唯一。
update_or_create()
¶
- update_or_create(defaults=None, create_defaults=None, **kwargs)¶
- aupdate_or_create(defaults=None, create_defaults=None, **kwargs)¶
非同步版本: aupdate_or_create()
這是一個方便的方法,可以使用給定的 kwargs
更新物件,必要時建立一個新物件。create_defaults
和 defaults
都是 (欄位, 值) 配對的字典。 create_defaults
和 defaults
中的值都可以是可呼叫的物件。defaults
用於更新物件,而 create_defaults
用於建立操作。如果沒有提供 create_defaults
,則會將 defaults
用於建立操作。
傳回一個 (object, created)
元組,其中 object
是已建立或更新的物件,而 created
是一個布林值,指定是否建立了新的物件。
update_or_create
方法會嘗試根據給定的 kwargs
從資料庫中提取物件。如果找到匹配項,它會更新 defaults
字典中傳入的欄位。
這是一個程式碼樣板的快捷方式。例如:
defaults = {"first_name": "Bob"}
create_defaults = {"first_name": "Bob", "birthday": date(1940, 10, 9)}
try:
obj = Person.objects.get(first_name="John", last_name="Lennon")
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {"first_name": "John", "last_name": "Lennon"}
new_values.update(create_defaults)
obj = Person(**new_values)
obj.save()
當模型中的欄位數量增加時,這種模式會變得相當笨拙。上面的範例可以使用 update_or_create()
像這樣重寫:
obj, created = Person.objects.update_or_create(
first_name="John",
last_name="Lennon",
defaults={"first_name": "Bob"},
create_defaults={"first_name": "Bob", "birthday": date(1940, 10, 9)},
)
有關 kwargs
中傳遞的名稱如何解析的詳細說明,請參閱 get_or_create()
。
如上文在 get_or_create()
中所述,此方法容易發生競爭條件,如果未在資料庫層級強制執行唯一性,可能會導致同時插入多個列。
與 get_or_create()
和 create()
一樣,如果您使用的是手動指定的 Primary Key,且需要建立物件,但該鍵已存在於資料庫中,則會引發 IntegrityError
。
已新增 create_defaults
引數。
bulk_create()
¶
- bulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)¶
- abulk_create(objs, batch_size=None, ignore_conflicts=False, update_conflicts=False, update_fields=None, unique_fields=None)¶
非同步版本: abulk_create()
此方法以有效的方式將提供的物件清單插入到資料庫中(通常只有 1 個查詢,無論有多少個物件),並將建立的物件以清單形式傳回,順序與提供的順序相同。
>>> objs = Entry.objects.bulk_create(
... [
... Entry(headline="This is a test"),
... Entry(headline="This is only a test"),
... ]
... )
不過,這有一些注意事項:
不會呼叫模型的
save()
方法,也不會傳送pre_save
和post_save
訊號。它不適用於多表格繼承情境中的子模型。
如果模型的主要鍵是
AutoField
且ignore_conflicts
為 False,則只能在某些資料庫(目前為 PostgreSQL、MariaDB 和 SQLite 3.35+)上檢索主要鍵屬性。在其他資料庫上,則不會設定。它不適用於多對多關係。
它會將
objs
轉換為清單,如果objs
是生成器,則會完全評估objs
。轉換允許檢查所有物件,以便可以先插入任何具有手動設定主要鍵的物件。如果您想分批插入物件而不一次評估整個生成器,則只要物件沒有任何手動設定的主要鍵,就可以使用此技術。from itertools import islice batch_size = 100 objs = (Entry(headline="Test %s" % i) for i in range(1000)) while True: batch = list(islice(objs, batch_size)) if not batch: break Entry.objects.bulk_create(batch, batch_size)
batch_size
參數控制在單一查詢中建立多少個物件。預設是在一個批次中建立所有物件,除了 SQLite,其預設值是每個查詢最多使用 999 個變數。
在支援它的資料庫(除了 Oracle 之外的所有資料庫)上,將 ignore_conflicts
參數設定為 True
會告知資料庫忽略插入任何違反約束(例如重複的唯一值)的列時發生的失敗。
在支援它的資料庫(除了 Oracle 之外的所有資料庫)上,將 update_conflicts
參數設定為 True
,會告知資料庫在列插入因衝突而失敗時更新 update_fields
。在 PostgreSQL 和 SQLite 上,除了 update_fields
之外,還必須提供可能發生衝突的 unique_fields
清單。
啟用 ignore_conflicts
參數會停用在每個模型實例上設定主要鍵(如果資料庫通常支援)。
在較舊版本中,啟用 update_conflicts
參數會阻止在每個模型實例上設定主要鍵。
警告
在 MySQL 和 MariaDB 上,將 ignore_conflicts
參數設定為 True
會將某些類型的錯誤(除了重複的鍵之外)變成警告。即使在嚴格模式下也是如此。例如:無效的值或不可為 null 的違規。如需更多詳細資訊,請參閱 MySQL 文件 和 MariaDB 文件。
bulk_update()
¶
- bulk_update(objs, fields, batch_size=None)¶
- abulk_update(objs, fields, batch_size=None)¶
非同步版本:abulk_update()
此方法有效率地更新提供的模型實例上指定的欄位,通常使用一個查詢,並回傳更新的物件數量。
>>> objs = [
... Entry.objects.create(headline="Entry 1"),
... Entry.objects.create(headline="Entry 2"),
... ]
>>> objs[0].headline = "This is entry 1"
>>> objs[1].headline = "This is entry 2"
>>> Entry.objects.bulk_update(objs, ["headline"])
2
QuerySet.update()
用於儲存變更,因此這比迭代模型列表並對每個模型呼叫 save()
更有效率,但有一些注意事項:
您無法更新模型的主鍵。
如果更新大量行中的大量欄位,產生的 SQL 可能會非常龐大。透過指定合適的
batch_size
來避免這種情況。更新在多表格繼承祖先上定義的欄位將為每個祖先產生額外的查詢。
當個別批次包含重複項時,只有該批次中的第一個實例會導致更新。
函數回傳的更新物件數量可能少於傳入的物件數量。這可能是因為傳入的重複物件在同一個批次中更新,或是競爭條件導致物件不再存在於資料庫中。
batch_size
參數控制在單一查詢中儲存多少物件。預設值是一次更新所有物件,但 SQLite 和 Oracle 對查詢中使用的變數數量有限制。
count()
¶
- count()¶
- acount()¶
非同步版本:acount()
回傳一個整數,表示資料庫中符合 QuerySet
的物件數量。
範例
# Returns the total number of entries in the database.
Entry.objects.count()
# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains="Lennon").count()
count()
呼叫會在幕後執行 SELECT COUNT(*)
,因此您應該始終使用 count()
,而不是將所有記錄載入到 Python 物件中並在結果上呼叫 len()
(除非您無論如何都需要將物件載入到記憶體中,在這種情況下,len()
會更快)。
請注意,如果您想要取得 QuerySet
中的項目數量,並且也從中檢索模型實例 (例如,透過迭代它),使用 len(queryset)
可能會更有效率,它不會像 count()
一樣導致額外的資料庫查詢。
如果 queryset 已完全檢索,count()
將使用該長度,而不是執行額外的資料庫查詢。
in_bulk()
¶
- in_bulk(id_list=None, *, field_name='pk')¶
- ain_bulk(id_list=None, *, field_name='pk')¶
非同步版本:ain_bulk()
接受欄位值列表 (id_list
) 和這些值的 field_name
,並回傳一個字典,將每個值對應到具有指定欄位值的物件實例。 in_bulk
永遠不會引發 django.core.exceptions.ObjectDoesNotExist
例外;也就是說,任何不符合任何實例的 id_list
值都會被簡單地忽略。如果未提供 id_list
,則會回傳 queryset 中的所有物件。field_name
必須是唯一欄位或不同欄位 (如果 distinct()
中只指定一個欄位)。field_name
預設為主鍵。
範例
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(["beatles_blog"], field_name="slug")
{'beatles_blog': <Blog: Beatles Blog>}
>>> Blog.objects.distinct("name").in_bulk(field_name="name")
{'Beatles Blog': <Blog: Beatles Blog>, 'Cheddar Talk': <Blog: Cheddar Talk>, 'Django Weblog': <Blog: Django Weblog>}
如果您傳遞一個空列表給 in_bulk()
,您將得到一個空字典。
iterator()
¶
- iterator(chunk_size=None)¶
- aiterator(chunk_size=None)¶
非同步版本:aiterator()
評估 QuerySet
(透過執行查詢) 並回傳結果上的迭代器 (請參閱 PEP 234),如果您呼叫其非同步版本 aiterator
,則回傳非同步迭代器 (請參閱 PEP 492)。
QuerySet
通常會在內部快取其結果,以便重複評估不會導致額外的查詢。 相反地,iterator()
將直接讀取結果,而不在 QuerySet
層級進行任何快取 (在內部,預設迭代器呼叫 iterator()
並快取回傳值)。 對於只須存取一次的大量物件的 QuerySet
而言,這可以帶來更好的效能和顯著的記憶體減少。
請注意,在已評估的 QuerySet
上使用 iterator()
將強制它再次評估,重複查詢。
只要指定 chunk_size
,iterator()
就與之前呼叫的 prefetch_related()
相容。較大的值將需要較少的查詢來完成預取,但會犧牲較多的記憶體使用量。
新增了對先前呼叫 prefetch_related()
的 aiterator()
的支援。
在某些資料庫 (例如 Oracle、SQLite) 中,SQL IN
子句中的最大項數可能有限制。 因此,應使用低於此限制的值。(特別是,當跨兩個或多個關聯進行預取時,chunk_size
應該足夠小,以便每個預取關聯的預期結果數量仍然低於限制。)
只要 QuerySet 不預取任何相關物件,不為 chunk_size
提供值將導致 Django 使用 2000 的隱式預設值。
根據資料庫後端,查詢結果將會一次全部載入,或是使用伺服器端游標從資料庫串流。
使用伺服器端游標¶
Oracle 和 PostgreSQL 使用伺服器端游標從資料庫串流結果,而不會將整個結果集載入記憶體。
Oracle 資料庫驅動程式始終使用伺服器端游標。
使用伺服器端游標時,chunk_size
參數指定在資料庫驅動程式層級要快取的結果數量。 擷取較大的區塊會減少資料庫驅動程式和資料庫之間的往返次數,但會犧牲記憶體。
在 PostgreSQL 上,只有當 DISABLE_SERVER_SIDE_CURSORS
設定為 False
時,才會使用伺服器端游標。 如果您使用的是以交易池模式配置的連線池,請閱讀交易池和伺服器端游標。當伺服器端游標停用時,其行為與不支援伺服器端游標的資料庫相同。
沒有伺服器端游標¶
MySQL 不支援串流結果,因此 Python 資料庫驅動程式會將整個結果集載入記憶體。然後,資料庫介面卡會使用 PEP 249 中定義的 fetchmany()
方法將結果集轉換為 Python 列物件。
SQLite 可以使用 fetchmany()
分批擷取結果,但由於 SQLite 不會在連線中的查詢之間提供隔離,因此在寫入正在迭代的資料表時請務必小心。有關更多資訊,請參閱使用 QuerySet.iterator() 時的隔離。
chunk_size
參數控制 Django 從資料庫驅動程式擷取的批次大小。較大的批次會減少與資料庫驅動程式通訊的開銷,但會稍微增加記憶體消耗。
只要 QuerySet 沒有預先擷取任何相關物件,如果沒有為 chunk_size
提供值,Django 將會使用隱含的預設值 2000,該值來自 psycopg 郵件列表上的計算
假設具有 10-20 個欄位且混合了文字和數字資料的列,2000 將擷取不到 100KB 的資料,這似乎是在傳輸的列數和如果提早退出迴圈而丟棄的資料之間取得良好的折衷方案。
latest()
¶
- latest(*fields)¶
- alatest(*fields)¶
非同步版本: alatest()
根據給定的欄位,傳回資料表中最新的物件。
此範例根據 pub_date
欄位傳回資料表中最新的 Entry
Entry.objects.latest("pub_date")
您也可以根據多個欄位選擇最新物件。 例如,若要選取當兩個條目具有相同的 pub_date
時,expire_date
最早的 Entry
Entry.objects.latest("pub_date", "-expire_date")
'-expire_date'
中的負號表示以遞減順序排序 expire_date
。由於 latest()
取得最後一個結果,因此會選取 expire_date
最早的 Entry
。
如果您的模型的 Meta 指定了 get_latest_by
,您可以省略 earliest()
或 latest()
的任何引數。預設會使用 get_latest_by
中指定的欄位。
與 get()
一樣,如果沒有具有給定參數的物件,earliest()
和 latest()
會引發 DoesNotExist
。
請注意,earliest()
和 latest()
的存在純粹是為了方便和可讀性。
earliest()
和 latest()
可能會傳回具有 Null 日期的實例。
由於排序委派給資料庫,因此如果您使用不同的資料庫,允許 Null 值的欄位上的結果可能會以不同的方式排序。例如,PostgreSQL 和 MySQL 將 Null 值排序為高於非 Null 值,而 SQLite 則相反。
您可能想要篩選掉 Null 值
Entry.objects.filter(pub_date__isnull=False).latest("pub_date")
earliest()
¶
- earliest(*fields)¶
- aearliest(*fields)¶
非同步版本: aearliest()
其他方面與 latest()
類似,只是方向改變。
first()
¶
- first()¶
- afirst()¶
非同步版本: afirst()
傳回 Queryset 比對的第一個物件,如果沒有符合的物件,則傳回 None
。如果 QuerySet
沒有定義排序,則會依主鍵自動排序 queryset。這可能會影響彙總結果,如與 order_by() 的互動中所述。
範例
p = Article.objects.order_by("title", "pub_date").first()
請注意,first()
是一個方便的方法,以下程式碼範例與上述範例等效
try:
p = Article.objects.order_by("title", "pub_date")[0]
except IndexError:
p = None
last()
¶
- last()¶
- alast()¶
非同步版本: alast()
與 first()
的工作方式類似,但會傳回 queryset 中的最後一個物件。
aggregate()
¶
- aggregate(*args, **kwargs)¶
- aaggregate(*args, **kwargs)¶
非同步版本: aaggregate()
傳回在 QuerySet
上計算的彙總值(平均值、總和等)的字典。 aggregate()
的每個引數都會指定將包含在傳回字典中的值。
Django 提供的彙總函式在下面的彙總函式中說明。由於彙總也是查詢表達式,因此您可以將彙總與其他彙總或值結合,以建立複雜的彙總。
使用關鍵字引數指定的彙總將使用關鍵字作為註釋的名稱。匿名引數將根據彙總函式的名稱和正在彙總的模型欄位為它們產生名稱。複雜的彙總不能使用匿名引數,並且必須指定關鍵字引數作為別名。
例如,當您處理部落格條目時,您可能想知道有多少作者撰寫了部落格條目
>>> from django.db.models import Count
>>> Blog.objects.aggregate(Count("entry"))
{'entry__count': 16}
透過使用關鍵字引數指定彙總函式,您可以控制傳回的彙總值的名稱
>>> Blog.objects.aggregate(number_of_entries=Count("entry"))
{'number_of_entries': 16}
有關彙總的深入討論,請參閱有關彙總的主題指南。
exists()
¶
- exists()¶
- aexists()¶
非同步版本: aexists()
如果 QuerySet
包含任何結果,則返回 True
,否則返回 False
。 該方法會嘗試以最簡單且最快的方式執行查詢,但它確實會執行與一般 QuerySet
查詢幾乎相同的查詢。
exists()
對於搜尋 QuerySet
中是否存在任何物件的情況非常有用,尤其是在大型 QuerySet
的情況下。
要查找 queryset 是否包含任何項目
if some_queryset.exists():
print("There is at least one object in some_queryset")
這將比以下方法快
if some_queryset:
print("There is at least one object in some_queryset")
…但差距不大(因此需要大型 queryset 才能提高效率)。
此外,如果 some_queryset
尚未被評估,但您知道它會在某個時間點被評估,那麼使用 some_queryset.exists()
將會執行更多整體工作 (一個用於存在檢查的查詢,加上一個額外的查詢來稍後擷取結果),而不是使用 bool(some_queryset)
,它會擷取結果,然後檢查是否有任何結果返回。
contains()
¶
- contains(obj)¶
- acontains(obj)¶
非同步版本:acontains()
如果 QuerySet
包含 obj
,則返回 True
,否則返回 False
。 該方法會嘗試以最簡單且最快的方式執行查詢。
contains()
對於檢查 QuerySet
中的物件成員資格非常有用,尤其是在大型 QuerySet
的情況下。
要檢查 queryset 是否包含特定項目
if some_queryset.contains(obj):
print("Entry contained in queryset")
這會比以下需要評估和遍歷整個 queryset 的方法更快
if obj in some_queryset:
print("Entry contained in queryset")
與 exists()
類似,如果 some_queryset
尚未被評估,但您知道它會在某個時間點被評估,那麼使用 some_queryset.contains(obj)
將會執行額外的資料庫查詢,通常會導致整體效能較慢。
update()
¶
- update(**kwargs)¶
- aupdate(**kwargs)¶
非同步版本:aupdate()
為指定的欄位執行 SQL 更新查詢,並返回匹配的行數(如果某些行已經具有新值,則該行數可能不等於更新的行數)。
例如,要關閉 2010 年發布的所有部落格文章的評論,您可以執行此操作
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
(這假設您的 Entry
模型具有 pub_date
和 comments_on
欄位。)
您可以更新多個欄位,數量沒有限制。 例如,這裡我們更新 comments_on
和 headline
欄位
>>> Entry.objects.filter(pub_date__year=2010).update(
... comments_on=False, headline="This is old"
... )
update()
方法會立即應用,並且更新的 QuerySet
的唯一限制是它只能更新模型主表中的欄位,而不能更新相關模型中的欄位。 例如,您不能這樣做
>>> Entry.objects.update(blog__name="foo") # Won't work!
不過,仍然可以根據相關欄位進行篩選
>>> Entry.objects.filter(blog__id=1).update(comments_on=True)
您不能在已取得切片或無法再進行篩選的 QuerySet
上呼叫 update()
。
update()
方法會返回受影響的行數
>>> Entry.objects.filter(id=64).update(comments_on=True)
1
>>> Entry.objects.filter(slug="nonexistent-slug").update(comments_on=True)
0
>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132
如果您只是更新一筆記錄,並且不需要對模型物件執行任何操作,那麼最有效的方法是呼叫 update()
,而不是將模型物件載入記憶體中。 例如,不要執行此操作
e = Entry.objects.get(id=10)
e.comments_on = False
e.save()
…執行此操作
Entry.objects.filter(id=10).update(comments_on=False)
使用 update()
還可以防止在載入物件和呼叫 save()
之間短暫的時間內,資料庫中可能會發生變更的競爭條件。
最後,請注意 update()
會在 SQL 層級執行更新,因此不會在您的模型上呼叫任何 save()
方法,也不會發出 pre_save
或 post_save
信號 (這是呼叫 Model.save()
的結果)。 如果您想要為具有自訂 save()
方法的模型更新大量記錄,請迴圈遍歷它們並呼叫 save()
,如下所示
for e in Entry.objects.filter(pub_date__year=2010):
e.comments_on = False
e.save()
排序的 queryset¶
僅在 MariaDB 和 MySQL 上支援將 order_by()
與 update()
鏈接,並且對於不同的資料庫會忽略。 這對於按照指定的順序更新唯一欄位而不發生衝突非常有用。 例如
Entry.objects.order_by("-number").update(number=F("number") + 1)
注意
如果 order_by()
子句包含註釋、繼承的欄位或跨關係的查找,則會被忽略。
delete()
¶
- delete()¶
- adelete()¶
非同步版本:adelete()
對 QuerySet
中的所有行執行 SQL 刪除查詢,並返回已刪除的物件數以及每個物件類型的刪除數的字典。
delete()
會立即應用。 您不能在已取得切片或無法再進行篩選的 QuerySet
上呼叫 delete()
。
例如,要刪除特定部落格中的所有條目
>>> b = Blog.objects.get(pk=1)
# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'blog.Entry': 2, 'blog.Entry_authors': 2})
依預設,Django 的 ForeignKey
會模擬 SQL 約束 ON DELETE CASCADE
,換句話說,任何具有指向要刪除物件的外鍵的物件也會與它們一起刪除。 例如
>>> blogs = Blog.objects.all()
# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'blog.Blog': 1, 'blog.Entry': 2, 'blog.Entry_authors': 2})
此串聯行為可透過 on_delete
引數自訂為 ForeignKey
。
delete()
方法會執行批量刪除,並且不會在您的模型上呼叫任何 delete()
方法。 不過,它會為所有已刪除的物件 (包括串聯刪除) 發出 pre_delete
和 post_delete
信號。
Django 需要將物件載入記憶體中,才能傳送信號並處理串聯。 但是,如果沒有串聯且沒有信號,那麼 Django 可能會採取快速路徑並刪除物件,而無需將其載入記憶體中。 對於大型刪除,這可以顯著減少記憶體使用量。 也可以減少執行的查詢數量。
設定為 on_delete
DO_NOTHING
的外鍵不會阻止在刪除時採用快速路徑。
請注意,物件刪除中產生的查詢是實作細節,可能會變更。
as_manager()
¶
- classmethod as_manager()¶
類別方法,會回傳一個 Manager
的實例,其中包含 QuerySet
方法的副本。更多詳細資訊請參閱 使用 QuerySet 方法建立管理器。
請注意,與本節中的其他條目不同,此方法沒有非同步變體,因為它不會執行查詢。
explain()
¶
- explain(format=None, **options)¶
- aexplain(format=None, **options)¶
非同步版本: aexplain()
回傳 QuerySet
執行計畫的字串,其中詳細說明了資料庫將如何執行查詢,包括將使用哪些索引或聯結。了解這些詳細資訊可以幫助您提高慢速查詢的效能。
例如,當使用 PostgreSQL 時
>>> print(Blog.objects.filter(title="My Blog").explain())
Seq Scan on blog (cost=0.00..35.50 rows=10 width=12)
Filter: (title = 'My Blog'::bpchar)
不同資料庫的輸出結果差異很大。
除了 Oracle 之外,所有內建資料庫後端都支援 explain()
,因為 Oracle 上的實作並不直接。
format
參數會將輸出格式從資料庫的預設值(通常是文字格式)變更。 PostgreSQL 支援 'TEXT'
、'JSON'
、'YAML'
和 'XML'
格式。 MariaDB 和 MySQL 支援 'TEXT'
(也稱為 'TRADITIONAL'
)和 'JSON'
格式。 MySQL 8.0.16+ 也支援改良的 'TREE'
格式,該格式類似於 PostgreSQL 的 'TEXT'
輸出,並且在支援的情況下預設使用。
某些資料庫接受可以回傳更多查詢資訊的旗標。將這些旗標作為關鍵字引數傳遞。例如,當使用 PostgreSQL 時
>>> print(Blog.objects.filter(title="My Blog").explain(verbose=True, analyze=True))
Seq Scan on public.blog (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
Output: id, title
Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms
在某些資料庫上,旗標可能會導致執行查詢,這可能會對您的資料庫產生不利影響。例如,MariaDB、MySQL 8.0.18+ 和 PostgreSQL 支援的 ANALYZE
旗標可能會導致資料變更,如果存在觸發器或呼叫了函數,即使是 SELECT
查詢也一樣。
新增了對 PostgreSQL 16+ 上 generic_plan
選項的支援。
Field
查找¶
欄位查找是您如何指定 SQL WHERE
子句主要內容的方式。它們被指定為 QuerySet
方法 filter()
、exclude()
和 get()
的關鍵字引數。
如需介紹,請參閱 模型和資料庫查詢文件。
以下列出 Django 的內建查找。也可以為模型欄位編寫 自訂查找。
為了方便起見,當未提供查找類型時(例如在 Entry.objects.get(id=14)
中),查找類型會被假設為 exact
。
exact
¶
完全匹配。如果提供的比較值為 None
,則會將其解釋為 SQL NULL
(如需更多詳細資訊,請參閱 isnull
)。
範例
Entry.objects.get(id__exact=14)
Entry.objects.get(id__exact=None)
SQL 等效項
SELECT ... WHERE id = 14;
SELECT ... WHERE id IS NULL;
iexact
¶
不區分大小寫的完全匹配。如果提供的比較值為 None
,則會將其解釋為 SQL NULL
(如需更多詳細資訊,請參閱 isnull
)。
範例
Blog.objects.get(name__iexact="beatles blog")
Blog.objects.get(name__iexact=None)
SQL 等效項
SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;
請注意,第一個查詢會匹配 'Beatles Blog'
、'beatles blog'
、'BeAtLes BLoG'
等。
SQLite 使用者
當使用 SQLite 後端和非 ASCII 字串時,請記住關於字串比較的 資料庫注意事項。SQLite 不會對非 ASCII 字串執行不區分大小寫的匹配。
contains
¶
區分大小寫的包含測試。
範例
Entry.objects.get(headline__contains="Lennon")
SQL 等效語法
SELECT ... WHERE headline LIKE '%Lennon%';
請注意,這會匹配標題 'Lennon honored today'
但不會匹配 'lennon honored today'
。
SQLite 使用者
SQLite 不支援區分大小寫的 LIKE
陳述式;對於 SQLite,contains
的作用類似於 icontains
。如需更多資訊,請參閱 資料庫注意事項。
icontains
¶
不區分大小寫的包含測試。
範例
Entry.objects.get(headline__icontains="Lennon")
SQL 等效語法
SELECT ... WHERE headline ILIKE '%Lennon%';
SQLite 使用者
當使用 SQLite 後端和非 ASCII 字串時,請記住關於字串比較的 資料庫注意事項。
in
¶
在給定的可迭代對象中;通常是列表、元組或 queryset。這不是常見的用例,但字串(屬於可迭代對象)是可以接受的。
範例
Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in="abc")
SQL 等效項
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
您也可以使用 queryset 來動態評估值列表,而不是提供字面值列表
inner_qs = Blog.objects.filter(name__contains="Cheddar")
entries = Entry.objects.filter(blog__in=inner_qs)
此 queryset 將評估為子查詢陳述式
SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
如果您將從 values()
或 values_list()
產生的 QuerySet
作為 __in
查找的值傳遞,則需要確保您僅在結果中提取一個欄位。例如,這會起作用(根據部落格名稱篩選)
inner_qs = Blog.objects.filter(name__contains="Ch").values("name")
entries = Entry.objects.filter(blog__name__in=inner_qs)
此範例會引發例外,因為內部查詢試圖提取兩個欄位值,但預期只有一個。
# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains="Ch").values("name", "id")
entries = Entry.objects.filter(blog__name__in=inner_qs)
效能考量
請謹慎使用巢狀查詢,並了解您的資料庫伺服器的效能特性(如有疑問,請進行基準測試!)。某些資料庫後端,尤其是 MySQL,並未很好地最佳化巢狀查詢。在這些情況下,更有效的方法是提取值列表,然後將其傳遞到第二個查詢中。也就是說,執行兩個查詢而不是一個
values = Blog.objects.filter(name__contains="Cheddar").values_list("pk", flat=True)
entries = Entry.objects.filter(blog__in=list(values))
請注意 Blog QuerySet
周圍的 list()
呼叫,以強制執行第一個查詢。如果沒有它,則會執行巢狀查詢,因為 QuerySet 是惰性的。
gt
¶
大於。
範例
Entry.objects.filter(id__gt=4)
SQL 等效語法
SELECT ... WHERE id > 4;
gte
¶
大於或等於。
lt
¶
小於。
lte
¶
小於或等於。
startswith
¶
區分大小寫的開頭比對。
範例
Entry.objects.filter(headline__startswith="Lennon")
SQL 等效語法
SELECT ... WHERE headline LIKE 'Lennon%';
SQLite 不支援區分大小寫的 LIKE
陳述式;對於 SQLite,startswith
的作用類似於 istartswith
。
istartswith
¶
不區分大小寫的開頭比對。
範例
Entry.objects.filter(headline__istartswith="Lennon")
SQL 等效語法
SELECT ... WHERE headline ILIKE 'Lennon%';
SQLite 使用者
當使用 SQLite 後端和非 ASCII 字串時,請記住關於字串比較的 資料庫注意事項。
endswith
¶
區分大小寫的結尾比對。
範例
Entry.objects.filter(headline__endswith="Lennon")
SQL 等效語法
SELECT ... WHERE headline LIKE '%Lennon';
SQLite 使用者
SQLite 不支援區分大小寫的 LIKE
陳述式;對於 SQLite 來說,endswith
的行為如同 iendswith
。請參考資料庫說明 文件以了解更多資訊。
iendswith
¶
不區分大小寫的結尾比對。
範例
Entry.objects.filter(headline__iendswith="Lennon")
SQL 等效語法
SELECT ... WHERE headline ILIKE '%Lennon'
SQLite 使用者
當使用 SQLite 後端和非 ASCII 字串時,請記住關於字串比較的 資料庫注意事項。
range
¶
範圍測試 (包含邊界值)。
範例
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
SQL 等效語法
SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
您可以在 SQL 中任何可以使用 BETWEEN
的地方使用 range
— 用於日期、數字甚至是字元。
警告
使用日期篩選 DateTimeField
不會包含最後一天的項目,因為邊界被解釋為「給定日期的午夜 0 點」。如果 pub_date
是 DateTimeField
,則上述表達式將會轉換為以下 SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
一般來說,您不能混合日期和日期時間。
date
¶
對於日期時間欄位,將值轉換為日期。允許串聯額外的欄位查詢。接受一個日期值。
範例
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
year
¶
對於日期和日期時間欄位,進行精確的年份比對。允許串聯額外的欄位查詢。接受一個整數年份。
範例
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)
SQL 等效語法
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';
(每個資料庫引擎的確切 SQL 語法有所不同。)
iso_year
¶
對於日期和日期時間欄位,進行精確的 ISO 8601 週數年份比對。允許串聯額外的欄位查詢。接受一個整數年份。
範例
Entry.objects.filter(pub_date__iso_year=2005)
Entry.objects.filter(pub_date__iso_year__gte=2005)
(每個資料庫引擎的確切 SQL 語法有所不同。)
month
¶
對於日期和日期時間欄位,進行精確的月份比對。允許串聯額外的欄位查詢。接受一個從 1 (一月) 到 12 (十二月) 的整數。
範例
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)
SQL 等效語法
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';
(每個資料庫引擎的確切 SQL 語法有所不同。)
day
¶
對於日期和日期時間欄位,進行精確的日期比對。允許串聯額外的欄位查詢。接受一個整數日期。
範例
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)
SQL 等效語法
SELECT ... WHERE EXTRACT('day' FROM pub_date) = '3';
SELECT ... WHERE EXTRACT('day' FROM pub_date) >= '3';
(每個資料庫引擎的確切 SQL 語法有所不同。)
請注意,這將會比對任何在該月份第三天有 pub_date 的記錄,例如 1 月 3 日、7 月 3 日等等。
week
¶
對於日期和日期時間欄位,根據 ISO-8601 回傳週數 (1-52 或 53),也就是說,週從星期一開始,且第一週包含該年的第一個星期四。
範例
Entry.objects.filter(pub_date__week=52)
Entry.objects.filter(pub_date__week__gte=32, pub_date__week__lte=38)
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
week_day
¶
對於日期和日期時間欄位,進行「星期幾」比對。允許串聯額外的欄位查詢。
接受一個整數值,代表從 1 (星期日) 到 7 (星期六) 的星期幾。
範例
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
請注意,這將會比對任何在星期一 (一週的第 2 天) 有 pub_date
的記錄,無論它發生在月份或年份。星期幾的索引,星期日為 1,星期六為 7。
iso_week_day
¶
對於日期和日期時間欄位,進行精確的 ISO 8601 星期幾比對。允許串聯額外的欄位查詢。
接受一個整數值,代表從 1 (星期一) 到 7 (星期日) 的星期幾。
範例
Entry.objects.filter(pub_date__iso_week_day=1)
Entry.objects.filter(pub_date__iso_week_day__gte=1)
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
請注意,這將會比對任何在星期一 (一週的第 1 天) 有 pub_date
的記錄,無論它發生在月份或年份。星期幾的索引,星期一為 1,星期日為 7。
quarter
¶
對於日期和日期時間欄位,進行「一年中的季度」比對。允許串聯額外的欄位查詢。接受一個介於 1 到 4 的整數值,代表一年中的季度。
範例,用來檢索第二季度 (4 月 1 日至 6 月 30 日) 的項目
Entry.objects.filter(pub_date__quarter=2)
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
time
¶
對於日期時間欄位,將值轉換為時間。允許串聯額外的欄位查詢。接受一個 datetime.time
值。
範例
Entry.objects.filter(pub_date__time=datetime.time(14, 30))
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))
(此查詢沒有包含等效的 SQL 程式碼片段,因為相關查詢的實作在不同的資料庫引擎之間有所不同。)
hour
¶
對於日期時間和時間欄位,進行精確的小時比對。允許串聯額外的欄位查詢。接受一個介於 0 和 23 之間的整數。
範例
Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)
SQL 等效語法
SELECT ... WHERE EXTRACT('hour' FROM timestamp) = '23';
SELECT ... WHERE EXTRACT('hour' FROM time) = '5';
SELECT ... WHERE EXTRACT('hour' FROM timestamp) >= '12';
(每個資料庫引擎的確切 SQL 語法有所不同。)
minute
¶
對於日期時間和時間欄位,進行精確的分鐘比對。允許串聯額外的欄位查詢。接受一個介於 0 和 59 之間的整數。
範例
Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)
SQL 等效語法
SELECT ... WHERE EXTRACT('minute' FROM timestamp) = '29';
SELECT ... WHERE EXTRACT('minute' FROM time) = '46';
SELECT ... WHERE EXTRACT('minute' FROM timestamp) >= '29';
(每個資料庫引擎的確切 SQL 語法有所不同。)
second
¶
對於日期時間和時間欄位,進行精確的秒鐘比對。允許串聯額外的欄位查詢。接受一個介於 0 和 59 之間的整數。
範例
Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)
SQL 等效語法
SELECT ... WHERE EXTRACT('second' FROM timestamp) = '31';
SELECT ... WHERE EXTRACT('second' FROM time) = '2';
SELECT ... WHERE EXTRACT('second' FROM timestamp) >= '31';
(每個資料庫引擎的確切 SQL 語法有所不同。)
isnull
¶
接受 True
或 False
,分別對應於 IS NULL
和 IS NOT NULL
的 SQL 查詢。
範例
Entry.objects.filter(pub_date__isnull=True)
SQL 等效語法
SELECT ... WHERE pub_date IS NULL;
regex
¶
區分大小寫的正規表示式比對。
正規表示式的語法為使用中的資料庫後端的語法。對於沒有內建正規表示式支援的 SQLite,此功能由 (Python) 使用者定義的 REGEXP 函式提供,因此正規表示式語法為 Python 的 re
模組的語法。
範例
Entry.objects.get(title__regex=r"^(An?|The) +")
SQL 等效項
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); -- Oracle
SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite
建議使用原始字串 (例如,使用 r'foo'
而不是 'foo'
) 來傳入正規表示式語法。
iregex
¶
不區分大小寫的正規表示式比對。
範例
Entry.objects.get(title__iregex=r"^(an?|the) +")
SQL 等效項
SELECT ... WHERE title REGEXP '^(an?|the) +'; -- MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(an?|the) +', 'i'); -- Oracle
SELECT ... WHERE title ~* '^(an?|the) +'; -- PostgreSQL
SELECT ... WHERE title REGEXP '(?i)^(an?|the) +'; -- SQLite
聚合函式¶
Django 在 django.db.models
模組中提供了以下聚合函式。如需如何使用這些聚合函式的詳細資訊,請參閱關於聚合的主題指南。請參閱Aggregate
文件以了解如何建立您的聚合。
警告
SQLite 無法直接處理日期/時間欄位的聚合。這是因為 SQLite 中沒有原生日期/時間欄位,而 Django 目前使用文字欄位來模擬這些功能。嘗試在 SQLite 中對日期/時間欄位使用聚合將會引發 NotSupportedError
。
空的查詢集或群組
當與空的 QuerySet
或群組一起使用時,聚合函式會回傳 None
。例如,如果 QuerySet
沒有包含任何項目,或是對於非空 QuerySet
中的任何空群組,則 Sum
聚合函式會回傳 None
而不是 0
。若要改為回傳另一個值,請定義 default
引數。Count
是此行為的例外;如果 QuerySet
為空,它會回傳 0
,因為 Count
不支援 default
引數。
所有聚合都有以下共同參數
expressions
¶
引用模型欄位、欄位轉換或查詢表示式的字串。
output_field
¶
一個可選的參數,代表回傳值的模型欄位
注意
當合併多種欄位類型時,如果所有欄位都是相同類型,Django 才能判斷 output_field
。否則,您必須自行提供 output_field
。
filter
¶
一個可選的 Q 物件
,用於篩選要彙總的列。
default
¶
一個可選的參數,允許指定當查詢集(或分組)不包含任何條目時要使用的預設值。
**extra
¶
關鍵字參數,可以為聚合產生的 SQL 提供額外的上下文。
Avg
¶
- class Avg(expression, output_field=None, distinct=False, filter=None, default=None, **extra)[原始碼]¶
傳回給定表達式的平均值,除非您指定不同的
output_field
,否則該表達式必須是數值。預設別名:
<欄位>__avg
傳回類型:如果輸入為
int
,則為float
,否則與輸入欄位相同,如果提供了output_field
,則為output_field
。如果查詢集或分組為空,則傳回default
。
- distinct¶
選填。如果
distinct=True
,則Avg
會傳回唯一值的平均值。這相當於 SQL 的AVG(DISTINCT <欄位>)
。預設值為False
。
Count
¶
Max
¶
Min
¶
StdDev
¶
Sum
¶
Variance
¶
- class Variance(expression, output_field=None, sample=False, filter=None, default=None, **extra)[原始碼]¶
傳回所提供表達式中資料的變異數。
預設別名:
<field>__variance
傳回類型:如果輸入為
int
,則為float
,否則與輸入欄位相同,如果提供了output_field
,則為output_field
。如果查詢集或分組為空,則傳回default
。
- sample¶
選用。預設情況下,
Variance
會傳回母體變異數。但是,如果sample=True
,則傳回值將會是樣本變異數。