要從數(shù)據(jù)庫(kù)檢索對(duì)象,要通過(guò)模型類的 ?Manager
?構(gòu)建一個(gè) ?QuerySet
?。
一個(gè) QuerySet 代表來(lái)自數(shù)據(jù)庫(kù)中對(duì)象的一個(gè)集合。它可以有0個(gè),1個(gè)或者多個(gè) ?filters. Filters
?,可以根據(jù)給定參數(shù)縮小查詢結(jié)果量。在 SQL 的層面上, ?QuerySet
?對(duì)應(yīng) ?SELECT
?語(yǔ)句,而?*filters*
?對(duì)應(yīng)類似 ?WHERE
?或 ?LIMIT
?的限制子句。
你能通過(guò)模型的 ?Manager
?獲取 ?QuerySet
?。每個(gè)模型至少有一個(gè) ?Manager
?,默認(rèn)名稱是 ?objects
?。像這樣直接通過(guò)模型類使用它:
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
注意:Managers
只能通過(guò)模型類訪問(wèn),而不是通過(guò)模型實(shí)例,目的是強(qiáng)制分離 “表級(jí)” 操作和 “行級(jí)” 操作。
?Manager
?是模型的 ?QuerySets
?主要來(lái)源。例如 ?Blog.objects.all()
? 返回了一個(gè) ?QuerySet
?,后者包含了數(shù)據(jù)庫(kù)中所有的 ?Blog
?對(duì)象。
從數(shù)據(jù)庫(kù)中檢索對(duì)象最簡(jiǎn)單的方式就是檢索全部。為此,在 ?Manager
?上調(diào)用 ?all()
? 方法:
>>> all_entries = Entry.objects.all()
方法 ?all()
? 返回了一個(gè)包含數(shù)據(jù)庫(kù)中所有對(duì)象的 ?QuerySet
?對(duì)象。
?all()
?返回的 ?QuerySet
?包含了數(shù)據(jù)表中所有的對(duì)象。雖然,大多數(shù)情況下,你只需要完整對(duì)象集合的一個(gè)子集。要?jiǎng)?chuàng)建一個(gè)這樣的子集,你需要通過(guò)添加過(guò)濾條件精煉原始 ?QuerySet
?。兩種最常見(jiàn)的精煉 ?QuerySet
?的方式是:
filter(**kwargs)
?返回一個(gè)新的 ?QuerySet
?,包含的對(duì)象滿足給定查詢參數(shù)。exclude(**kwargs)
?返回一個(gè)新的 ?QuerySet
?,包含的對(duì)象不滿足給定查詢參數(shù)。例如,要包含獲取 2006 年的博客條目(entries blog)的 ?QuerySet
?,像這樣使用 ?filter()
?:
Entry.objects.filter(pub_date__year=2006)
通過(guò)默認(rèn)管理器類也一樣:
Entry.objects.all().filter(pub_date__year=2006)
精煉 ?QuerySet
?的結(jié)果本身還是一個(gè) ?QuerySet
?,所以能串聯(lián)精煉過(guò)程。例子:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime.date(2005, 1, 30)
... )
這個(gè)先獲取包含數(shù)據(jù)庫(kù)所有條目(?entry
?)的 ?QuerySet
?,然后排除一些,再進(jìn)入另一個(gè)過(guò)濾器。最終的 ?QuerySet
?包含標(biāo)題以 ?"What"
? 開(kāi)頭的,發(fā)布日期介于 2005 年 1 月 30 日與今天之間的所有條目。
每次精煉一個(gè) ?QuerySet
?,你就會(huì)獲得一個(gè)全新的 ?QuerySet
?,后者與前者毫無(wú)關(guān)聯(lián)。每次精煉都會(huì)創(chuàng)建一個(gè)單獨(dú)的、不同的 ?QuerySet
?,能被存儲(chǔ),使用和復(fù)用。
舉例:
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
這三個(gè) ?QuerySets
?是獨(dú)立的。第一個(gè)是基礎(chǔ) ?QuerySet
?,包含了所有標(biāo)題以 ?"What"
? 開(kāi)頭的條目。第二個(gè)是第一個(gè)的子集,帶有額外條件,排除了 ?pub_date
?是今天和今天之后的所有記錄。第三個(gè)是第一個(gè)的子集,帶有額外條件,只篩選 ?pub_date
?是今天或未來(lái)的所有記錄。最初的 ?QuerySet (q1)
? 不受篩選操作影響。
?QuerySet
?是惰性的 —— 創(chuàng)建 ?QuerySet
?并不會(huì)引發(fā)任何數(shù)據(jù)庫(kù)活動(dòng)。你可以將一整天的過(guò)濾器都堆積在一起,Django 只會(huì)在 ?QuerySet
?被計(jì)算時(shí)執(zhí)行查詢操作。來(lái)瞄一眼這個(gè)例子:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
雖然這看起來(lái)像是三次數(shù)據(jù)庫(kù)操作,實(shí)際上只在最后一行 ?(print(q))
? 做了一次。一般來(lái)說(shuō), ?QuerySet
?的結(jié)果直到你 要使用時(shí)才會(huì)從數(shù)據(jù)庫(kù)中拿出。當(dāng)你要用時(shí),才通過(guò)數(shù)據(jù)庫(kù)計(jì)算出 ?QuerySet
?。
?filter()
? 總是返回一個(gè) ?QuerySet
?,即便只有一個(gè)對(duì)象滿足查詢條件 —— 這種情況下, ?QuerySet
?只包含了一個(gè)元素。
若你知道只會(huì)有一個(gè)對(duì)象滿足查詢條件,你可以在 ?Manager
?上使用 ?get()
? 方法,它會(huì)直接返回這個(gè)對(duì)象:
>>> one_entry = Entry.objects.get(pk=1)
你可以對(duì) ?get()
? 使用與 ?filter()
?類似的所有查詢表達(dá)式。
注意, 使用切片 [0] 時(shí)的 ?get()
? 和 ?filter()
? 有點(diǎn)不同。如果沒(méi)有滿足查詢條件的結(jié)果, ?get()
? 會(huì)拋出一個(gè) ?DoesNotExist
?異常。該異常是執(zhí)行查詢的模型類的一個(gè)屬性 —— 所有,上述代碼中,若沒(méi)有哪個(gè) ?Entry
?對(duì)象的主鍵是 1,Django 會(huì)拋出 ?Entry.DoesNotExist
?。
類似了,Django 會(huì)在有不止一個(gè)記錄滿足 ?get()
?查詢條件時(shí)發(fā)出警告。這時(shí),Django 會(huì)拋出 ?MultipleObjectsReturned
?,這同樣也是模型類的一個(gè)屬性。
大多數(shù)情況下,你會(huì)在需要從數(shù)據(jù)庫(kù)中檢索對(duì)象時(shí)使用 ?all()
?, ?get()
?, ?filter()
? 和 ?exclude()
?。
利用 Python 的數(shù)組切片語(yǔ)法將 ?QuerySet
?切成指定長(zhǎng)度。這等價(jià)于 SQL 的 ?LIMIT
?和 ?OFFSET
?子句。
例如,這將返回前 5 個(gè)對(duì)象 (?LIMIT 5
?):
>>> Entry.objects.all()[:5]
這會(huì)返回第 6 至第 10 個(gè)對(duì)象 (?OFFSET 5 LIMIT 5
?):
>>> Entry.objects.all()[5:10]
不支持負(fù)索引 (例如 ?Entry.objects.all()[-1]
?)
一般情況下, ?QuerySet
?的切片返回一個(gè)新的 ?QuerySet
?—— 其并未執(zhí)行查詢。一個(gè)特殊情況是使用了的 Python 切片語(yǔ)法的步長(zhǎng)。例如,這將會(huì)實(shí)際的執(zhí)行查詢命令,為了獲取從前 10 個(gè)對(duì)象中,每隔一個(gè)抽取的對(duì)象組成的列表:
>>> Entry.objects.all()[:10:2]
由于對(duì) ?queryset
?切片工作方式的模糊性,禁止對(duì)其進(jìn)行進(jìn)一步的排序或過(guò)濾。
要檢索 單個(gè) 對(duì)象而不是一個(gè)列表時(shí)(例如 ?SELECT foo FROM bar LIMIT 1
?),請(qǐng)使用索引,而不是切片。例如,這會(huì)返回按標(biāo)題字母排序后的第一個(gè) ?Entry
?:
>>> Entry.objects.order_by('headline')[0]
這大致等價(jià)于:
>>> Entry.objects.order_by('headline')[0:1].get()
然而,注意一下,若沒(méi)有對(duì)象滿足給定條件,前者會(huì)拋出 ?IndexError
?,而后者會(huì)拋出 ?DoesNotExist
?。
字段查詢即你如何制定 SQL ?WHERE
?子句。它們以關(guān)鍵字參數(shù)的形式傳遞給 ?QuerySet
?方法? filter()
?, ?exclude()
? 和 ?get()
?。
基本的查詢關(guān)鍵字參數(shù)遵照 ?field__lookuptype=value
?。(有個(gè)雙下劃線)。例如:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
轉(zhuǎn)換為 SQL 語(yǔ)句大致如下:
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
查詢子句中指定的字段必須是模型的一個(gè)字段名。不過(guò)也有個(gè)例外,在 ?ForeignKey
?中,你可以指定以 ?_id
? 為后綴的字段名。這種情況下,?value
參數(shù)需要包含 ?foreign
?模型的主鍵的原始值。例子:
>>> Entry.objects.filter(blog_id=4)
若你傳入了無(wú)效的關(guān)鍵字參數(shù),查詢函數(shù)會(huì)拋出 ?TypeError
?。
數(shù)據(jù)庫(kù) API 支持兩套查詢類型。為了讓你了解能干啥,以下是一些常見(jiàn)的查詢:
一個(gè) exact
? 匹配的例子:
>>> Entry.objects.get(headline__exact="Cat bites dog")
會(huì)生成這些 SQL:
SELECT ... WHERE headline = 'Cat bites dog';
若你為提供查詢類型 —— 也就說(shuō),若關(guān)鍵字參數(shù)未包含雙下劃線 —— 查詢類型會(huì)被指定為 ?exact
?。
例如,以下兩條語(yǔ)句是等價(jià)的:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
這是為了方便,因?yàn)??exact
?查詢是最常見(jiàn)的。
不分大小寫的匹配,查詢語(yǔ)句:
>>> Blog.objects.get(name__iexact="beatles blog")
會(huì)匹配標(biāo)題為 ?"Beatles Blog"
?, ?"beatles blog"
?, 甚至 ?"BeAtlES blOG"
? 的 ?Blog
?。
大小寫敏感的包含測(cè)試。例子:
Entry.objects.get(headline__contains='Lennon')
粗略地轉(zhuǎn)為 SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
注意這將匹配標(biāo)題 '?Today Lennon honored'
?,而不是 ?'today lennon honored'
?。
這也有個(gè)大小寫不敏感的版本, ?icontains
?。
?startswith
?, ?endswith
?
分別以搜索開(kāi)始和以搜索結(jié)束。
還有不區(qū)分大小寫的版本,稱為 ?isstartswith
?和 ?iendswith
?。
Django 提供了一種強(qiáng)大而直觀的方式來(lái)追蹤查詢中的關(guān)系,在幕后自動(dòng)為你處理 SQL ?JOIN
?關(guān)系。為了跨越關(guān)系,跨模型使用關(guān)聯(lián)字段名,字段名由雙下劃線分割,直到拿到想要的字段。
本例檢索出所有的 ?Entry
?對(duì)象,其 ?Blog
?的 ?name
?為 ?'Beatles Blog'
? :
>>> Entry.objects.filter(blog__name='Beatles Blog')
跨域的深度隨你所想。
它也可以反向工作。雖然它可以自定義,默認(rèn)情況下,你在查找中使用模型的小寫名稱來(lái)引用一個(gè)反向關(guān)系。
本例檢索的所有 ?Blog
?對(duì)象均擁有至少一個(gè) 標(biāo)題 含有 ?'Lennon'
? 的條目:
>>> Blog.objects.filter(entry__headline__contains='Lennon')
如果你在跨多個(gè)關(guān)系進(jìn)行篩選,而某個(gè)中間模型的沒(méi)有滿足篩選條件的值,Django 會(huì)將它當(dāng)做一個(gè)空的(所有值都是 ?NULL
?)但是有效的對(duì)象。這樣就意味著不會(huì)拋出錯(cuò)誤。例如,在這個(gè)過(guò)濾器中:
Blog.objects.filter(entry__authors__name='Lennon')
(假設(shè)有個(gè)關(guān)聯(lián)的 ?Author
?模型),若某項(xiàng)條目沒(méi)有任何關(guān)聯(lián)的 ?author
?,它會(huì)被視作沒(méi)有關(guān)聯(lián)的 ?name
?,而不是因?yàn)槿笔??author
?而拋出錯(cuò)誤。大多數(shù)情況下,這就是你期望的。唯一可能使你迷惑的場(chǎng)景是在使用 ?isnull
?時(shí)。因此:
Blog.objects.filter(entry__authors__name__isnull=True)
將會(huì)返回 ?Blog
?對(duì)象,包含 ?author
?的 ?name
?為空的對(duì)象,以及那些 ?entry
?的 ?author
?為空的對(duì)象。若你不想要后面的對(duì)象,你可以這樣寫:
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
當(dāng)跨越 ?ManyToManyField
?或反查 ?ForeignKey
?(例如從 ?Blog
?到 ?Entry
?)時(shí),對(duì)多個(gè)屬性進(jìn)行過(guò)濾會(huì)產(chǎn)生這樣的問(wèn)題:是否要求每個(gè)屬性都在同一個(gè)相關(guān)對(duì)象中重合。我們可能會(huì)尋找那些在標(biāo)題中含有 ?“Lennon”
? 的 2008 年的博客,或者我們可能會(huì)尋找那些僅有 2008 年的任何條目以及一些在標(biāo)題中含有 ?“Lennon”
? 的較新或較早的條目。
要選擇所有包含 2008 年至少一個(gè)標(biāo)題中有? "Lennon"
? 的條目的博客(滿足兩個(gè)條件的同一條目),我們要寫:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
否則,如果要執(zhí)行一個(gè)更為寬松的查詢,選擇任何只在標(biāo)題中帶有 ?"Lennon"
? 的條目和 2008 年的條目的博客,我們將寫:
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
假設(shè)只有一個(gè)博客既有包含 ?"Lennon"
? 的條目又有 2008 年的條目,但 2008 年的條目中沒(méi)有包含 ?"Lennon"
? 。第一個(gè)查詢不會(huì)返回任何博客,但第二個(gè)查詢會(huì)返回那一個(gè)博客。(這是因?yàn)榈诙€(gè)過(guò)濾器選擇的條目可能與第一個(gè)過(guò)濾器中的條目相同,也可能不相同)。我們是用每個(gè)過(guò)濾器語(yǔ)句來(lái)過(guò)濾 ?Blog
?項(xiàng),而不是 ?Entry
?項(xiàng))。簡(jiǎn)而言之,如果每個(gè)條件需要匹配相同的相關(guān)對(duì)象,那么每個(gè)條件應(yīng)該包含在一個(gè) ?filter()
? 調(diào)用中。
由于第二個(gè)查詢鏈接了多個(gè)過(guò)濾器,它對(duì)主模型進(jìn)行了多次連接,可能會(huì)產(chǎn)生重復(fù)的結(jié)果。
>>> from datetime import date
>>> beatles = Blog.objects.create(name='Beatles Blog')
>>> pop = Blog.objects.create(name='Pop Music Blog')
>>> Entry.objects.create(
... blog=beatles,
... headline='New Lennon Biography',
... pub_date=date(2008, 6, 1),
... )
<Entry: New Lennon Biography>
>>> Entry.objects.create(
... blog=beatles,
... headline='New Lennon Biography in Paperback',
... pub_date=date(2009, 6, 1),
... )
<Entry: New Lennon Biography in Paperback>
>>> Entry.objects.create(
... blog=pop,
... headline='Best Albums of 2008',
... pub_date=date(2008, 12, 15),
... )
<Entry: Best Albums of 2008>
>>> Entry.objects.create(
... blog=pop,
... headline='Lennon Would Have Loved Hip Hop',
... pub_date=date(2020, 4, 1),
... )
<Entry: Lennon Would Have Loved Hip Hop>
>>> Blog.objects.filter(
... entry__headline__contains='Lennon',
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>]>
>>> Blog.objects.filter(
... entry__headline__contains='Lennon',
... ).filter(
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>, <Blog: Beatles Blog>, <Blog: Pop Music Blog]>
注解:
?filter()
? 的查詢行為會(huì)跨越多值關(guān)聯(lián),就像前文說(shuō)的那樣,并不與 ?exclude()
? 相同。相反,一次 ?exclude()
? 調(diào)用的條件并不需要指向同一項(xiàng)目。
例如,以下查詢會(huì)排除那些關(guān)聯(lián)條目標(biāo)題包含 ?"Lennon"
? 且發(fā)布于 2008 年的博客:
Blog.objects.exclude(
entry__headline__contains='Lennon',
entry__pub_date__year=2008,
)
但是,與? filter()
? 的行為不同,其并不會(huì)限制博客同時(shí)滿足這兩種條件。要這么做的話,也就是篩選出所有條目標(biāo)題不帶 ?"Lennon"
?且發(fā)布年不是 2008 的博客,你需要做兩次查詢:
Blog.objects.exclude(
entry__in=Entry.objects.filter(
headline__contains='Lennon',
pub_date__year=2008,
),
)
在之前的例子中,我們已經(jīng)構(gòu)建過(guò)的 ?filter
?都是將模型字段值與常量做比較。但是,要怎么做才能將模型字段值與同一模型中的另一字段做比較呢?
Django 提供了 ?F
? 表達(dá)式 實(shí)現(xiàn)這種比較。 ?F()
? 的實(shí)例充當(dāng)查詢中的模型字段的引用。這些引用可在查詢過(guò)濾器中用于在同一模型實(shí)例中比較兩個(gè)不同的字段。
例如,要查出所有評(píng)論數(shù)大于 ?pingbacks
?的博客條目,我們構(gòu)建了一個(gè) ?F()
?對(duì)象,指代 ?pingback
?的數(shù)量,然后在查詢中使用該 ?F()
?對(duì)象:
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
Django 支持對(duì) ?F()
? 對(duì)象進(jìn)行加、減、乘、除、求余和次方,另一操作數(shù)既可以是常量,也可以是其它 F() 對(duì)象。要找到那些評(píng)論數(shù)兩倍于 ?pingbacks
的博客條目,我們這樣修改查詢條件:
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)
要找出所有評(píng)分低于 pingback 和評(píng)論總數(shù)之和的條目,修改查詢條件:
>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))
你也能用雙下劃線在 ?F()
? 對(duì)象中通過(guò)關(guān)聯(lián)關(guān)系查詢。帶有雙下劃線的 ?F()
? 對(duì)象將引入訪問(wèn)關(guān)聯(lián)對(duì)象所需的任何連接。例如,要檢索出所有作者名與博客名相同的博客,這樣修改查詢條件:
>>> Entry.objects.filter(authors__name=F('blog__name'))
對(duì)于 ?date
?和 ?date/time
? 字段,你可以加上或減去一個(gè) ?timedelta
?對(duì)象。以下會(huì)返回所有發(fā)布 3 天后被修改的條目:
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
?F()
? 對(duì)象通過(guò) ?.bitand()
?, ?.bitor()
?, ?.bitxor()
?,?.bitrightshift()
? 和? .bitleftshift()
? 支持位操作。例如:
>>> F('somefield').bitand(16)
Django 支持在表達(dá)式中使用轉(zhuǎn)換。
例如,要查找與上次修改同一年發(fā)布的所有條目對(duì)象:
>>> Entry.objects.filter(pub_date__year=F('mod_date__year'))
要查找條目發(fā)布的最早年份,我們可以發(fā)出查詢:
>>> Entry.objects.aggregate(first_published_year=Min('pub_date__year'))
此示例查找最高評(píng)分條目的值以及每年所有條目的評(píng)論總數(shù):
>>> Entry.objects.values('pub_date__year').annotate(
... top_rating=Subquery(
... Entry.objects.filter(
... pub_date__year=OuterRef('pub_date__year'),
... ).order_by('-rating').values('rating')[:1]
... ),
... total_comments=Sum('number_of_comments'),
... )
出于方便的目的,Django 提供了一種 ?pk
?查詢快捷方式, ?pk
?表示主鍵 ?"primary key"
?。
示例 ?Blog
?模型中,主鍵是 ?id
?字段,所以這 3 個(gè)語(yǔ)句是等效的:
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
?pk
?的使用并不僅限于? __exact
? 查詢——任何的查詢項(xiàng)都能接在 ?pk
?后面,執(zhí)行對(duì)模型主鍵的查詢:
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)
?pk
?查找也支持跨連接。例如,以下 3 個(gè)語(yǔ)句是等效的:
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3) # __exact is implied
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
等效于 ?LIKE
?SQL 語(yǔ)句的字段查詢子句 (?iexact
?, ?contains
?, ?icontains
?, ?startswith
?, ?istartswith
?, ?endswith
?和 ?iendswith
?) 會(huì)將 ?LIKE
?語(yǔ)句中有特殊用途的兩個(gè)符號(hào),即百分號(hào)和下劃線自動(dòng)轉(zhuǎn)義。(在 ?LIKE
?語(yǔ)句中,百分號(hào)匹配多個(gè)任意字符,而下劃線匹配一個(gè)任意字符。)
例如,要檢索所有包含百分號(hào)的條目:
>>> Entry.objects.filter(headline__contains='%')
Django 為你處理了引號(hào);生成的 SQL 語(yǔ)句看起來(lái)像這樣:
SELECT ... WHERE headline LIKE '%\%%';
同樣的處理也包括下劃線。百分號(hào)和下劃線都為你自動(dòng)處理,你無(wú)需擔(dān)心。
每個(gè) ?QuerySet
?都帶有緩存,盡量減少數(shù)據(jù)庫(kù)訪問(wèn)。理解它是如何工作的能讓你編寫更高效的代碼。
新創(chuàng)建的 ?QuerySet
?緩存是空的。一旦要計(jì)算 ?QuerySet
?的值,就會(huì)執(zhí)行數(shù)據(jù)查詢,隨后,Django 就會(huì)將查詢結(jié)果保存在 ?QuerySet
?的緩存中,并返回這些顯式請(qǐng)求的緩存(例如,下一個(gè)元素,若 ?QuerySet
?正在被迭代)。后續(xù)針對(duì) ?QuerySet
?的計(jì)算會(huì)復(fù)用緩存結(jié)果。
牢記這種緩存行為,在你錯(cuò)誤使用 ?QuerySet
?時(shí)可能會(huì)被它咬一下。例如,以下會(huì)創(chuàng)建兩個(gè) ?QuerySet
?,計(jì)算它們,丟掉它們:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
這意味著同樣的數(shù)據(jù)庫(kù)查詢會(huì)被執(zhí)行兩次,實(shí)際加倍了數(shù)據(jù)庫(kù)負(fù)載。同時(shí),有可能這兩個(gè)列表不包含同樣的記錄,因?yàn)樵趦纱握?qǐng)求間,可能有 ?Entry
?被添加或刪除了。
要避免此問(wèn)題,保存 ?QuerySet
?并復(fù)用它:
>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
查詢結(jié)果集并不總是緩存結(jié)果。當(dāng)僅計(jì)算查詢結(jié)果集的 部分 時(shí),會(huì)校驗(yàn)緩存,若沒(méi)有填充緩存,則后續(xù)查詢返回的項(xiàng)目不會(huì)被緩存。特別地說(shuō),這意味著使用數(shù)組切片或索引的 限制查詢結(jié)果集 不會(huì)填充緩存。
例如,重復(fù)的從某個(gè)查詢結(jié)果集對(duì)象中取指定索引的對(duì)象會(huì)每次都查詢數(shù)據(jù)庫(kù):
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
不過(guò),若全部查詢結(jié)果集已被檢出,就會(huì)去檢查緩存:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache
以下展示一些例子,這些動(dòng)作會(huì)觸發(fā)計(jì)算全部的查詢結(jié)果集,并填充緩存的過(guò)程:
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
注意:只是打印查詢結(jié)果集不會(huì)填充緩存。因?yàn)檎{(diào)用 ?__repr__()
?僅返回了完整結(jié)果集的一個(gè)切片。
更多建議: