原文:https://my.oschina.net/liuyuantao/blog/751438
查詢集API 參考
本文檔描述查詢集 API 的細節(jié)。它建立在模型和數(shù)據(jù)庫查詢指南的基礎(chǔ)上先嬉,所以在閱讀本文檔之前,你也許需要首先閱讀這兩部分的文檔。
本文檔將通篇使用在數(shù)據(jù)庫查詢指南中用到的Weblog 模型的例子。
何時對查詢集求值
在內(nèi)部已烤,可以創(chuàng)建、過濾妓羊、切片和傳遞查詢集而不用真實操作數(shù)據(jù)庫胯究。在你對查詢集做求值之前,不會發(fā)生任何實際的數(shù)據(jù)庫操作躁绸。
你可以使用下列方法對查詢集求值:
迭代裕循。查詢集是可迭代的臣嚣,它在首次迭代查詢集時執(zhí)行實際的數(shù)據(jù)庫查詢。例如剥哑, 下面的語句會將數(shù)據(jù)庫中所有Entry 的headline 打印出來:
foreinEntry.objects.all():print(e.headline)
注意:不要使用上面的語句來驗證在數(shù)據(jù)庫中是否至少存在一條記錄硅则。使用 exists()方法更高效。
切片株婴。 正如在限制查詢集中解釋的那樣怎虫, 可以使用Python 的序列切片語法對一個查詢集進行分片。一個未求值的查詢集進行切片通常返回另一個未求值的查詢集困介,但是如果你使用切片的”step“參數(shù)大审,Django 將執(zhí)行數(shù)據(jù)庫查詢并返回一個列表。對一個已經(jīng)求值的查詢集進行切片將返回一個列表座哩。
還要注意徒扶,雖然對未求值的查詢集進行切片返回另一個未求值的查詢集,但是卻不可以進一步修改它了(例如根穷,添加更多的Filter姜骡,或者修改排序的方式),因為這將不太好翻譯成SQL而且含義也不清晰缠诅。
序列化/緩存溶浴。 序列化查詢集的細節(jié)參見下面一節(jié)。本節(jié)提到它的目的是強調(diào)序列化將讀取數(shù)據(jù)庫管引。
repr()士败。 當對查詢集調(diào)用repr() 時,將對它求值褥伴。這是為了在Python 交互式解釋器中使用的方便谅将,這樣你可以在交互式使用這個API 時立即看到結(jié)果。
len()重慢。 當你對查詢集調(diào)用len() 時饥臂, 將對它求值。正如你期望的那樣似踱,返回一個查詢結(jié)果集的長度隅熙。
注:如果你只需要知道集合中記錄的個數(shù)(并不需要真實的對象),使用數(shù)據(jù)庫層級的SELECT COUNT(*) 計數(shù)將更加高效核芽。為此囚戚,Django 提供了 一個count() 方法.
list()。 對查詢集調(diào)用list() 將強制對它求值轧简。例如:
entry_list = list(Entry.objects.all())
bool()驰坊。 測試一個查詢集的布爾值,例如使用bool()哮独、or拳芙、and 或者if 語句將導致查詢集的執(zhí)行察藐。如果至少有一個記錄,則查詢集為True舟扎,否則為False分飞。例如:
ifEntry.objects.filter(headline="Test"):print("There is at least one Entry with the headline Test")
注:如果你需要知道是否存在至少一條記錄(而不需要真實的對象),使用 exists() 將更加高效浆竭。
Pickle 查詢集
如果你Pickle一個查詢集浸须,它將在Pickle 之前強制將所有的結(jié)果加載到內(nèi)存中。Pickle 通常用于緩存之前邦泄,并且當緩存的查詢集重新加載時删窒,你希望結(jié)果已經(jīng)存在隨時準備使用(從數(shù)據(jù)庫讀取耗費時間,就失去了緩存的目的)顺囊。這意味著當你Unpickle查詢集時肌索,它包含Pickle 時的結(jié)果,而不是當前數(shù)據(jù)庫中的結(jié)果特碳。
如果此后你只想Pickle 必要的信息來從數(shù)據(jù)庫重新創(chuàng)建查詢集诚亚,可以Pickle查詢集的query 屬性。然后你可以使用類似下面的代碼重新創(chuàng)建原始的查詢集(不用加載任何結(jié)果):
>>>import pickle>>>query = pickle.loads(s)# Assuming 's' is the pickled string.>>>qs = MyModel.objects.all()>>>qs.query = query# Restore the original 'query'.
query 是一個不透明的對象午乓。它表示查詢的內(nèi)部構(gòu)造站宗,不屬于公開的API。然而益愈,這里講到的Pickle 和Unpickle 這個屬性的內(nèi)容是安全的(和完全支持的)梢灭。
不可以在不同版本之間共享Pickle 的結(jié)果
查詢集的Pickle 只能用于生成它們的Django 版本中。如果你使用Django 的版本N 生成一個Pickle蒸其,不保證這個Pickle 在Django 的版本N+1 中可以讀取敏释。Pickle 不可用于歸檔的長期策略。
New in Django 1.8.
因為Pickle 兼容性的錯誤很難診斷例如產(chǎn)生損壞的對象摸袁,當你試圖Unpickle 的查詢集與Pickle 時的Django 版本不同荣月,將引發(fā)一個RuntimeWarning宪塔。
查詢集 API
下面是對于查詢集的正式定義:
classQuerySet([model=None,query=None,using=None])[source]
通常你在使用QuerySet時會以鏈式的filter 來使用。為了讓這個能工作喂柒,大部分QuerySet 方法返回新的QuerySet婴程。這些方法在本節(jié)將詳細講述庄敛。
QuerySet 類具有兩個公有屬性用于內(nèi)示瓢Α:
ordered[source]
如果QuerySet 是排好序的則為True —— 例如有一個order_by() 子句或者模型有默認的排序墙基。否則為False .
db[source]
如果現(xiàn)在執(zhí)行,則返回將使用的數(shù)據(jù)庫添谊。
注
QuerySet 存在query 參數(shù)是為了讓具有特殊查詢用途的子類如GeoQuerySet 可以重新構(gòu)造內(nèi)部的查詢狀態(tài)。這個參數(shù)的值是查詢狀態(tài)的不透明的表示察迟,不是一個公開的API斩狱。簡而言之:如果你有疑問耳高,那么你實際上不需要使用它。
返回新的查詢集 的方法
Django 提供了一系列 的QuerySet篩選方法所踊,用于改變 QuerySet 返回的結(jié)果類型或者SQL查詢執(zhí)行的方式泌枪。
filter
filter(**kwargs)
返回一個新的QuerySet,包含與給定的查詢參數(shù)匹配的對象秕岛。
查找的參數(shù)(**kwargs)應(yīng)該滿足下文字段查找中的格式碌燕。在底層的SQL 語句中,多個參數(shù)通過AND 連接继薛。
如果你需要執(zhí)行更復雜的查詢(例如修壕,使用OR 語句查詢),你可以使用Q 對象遏考。
exclude
exclude(**kwargs)
返回一個新的QuerySet慈鸠,它包含不滿足給定的查找參數(shù)的對象。
查找的參數(shù)(**kwargs)應(yīng)該滿足下文字段查找中的格式灌具。 在底層的SQL 語句中青团,多個參數(shù)通過AND 連接,然后所有的內(nèi)容放入NOT() 中咖楣。
下面的示例排除所有pub_date 晚于2005-1-3 且headline 為“Hello” 的記錄:
Entry.objects.exclude(pub_date__gt=datetime.date(2005,1,3), headline='Hello')
用SQL 語句督笆,它等同于:
SELECT...WHERENOT(pub_date >'2005-1-3'ANDheadline ='Hello')
下面的示例排除所有pub_date 晚于2005-1-3 或者headline 為“Hello” 的記錄:
Entry.objects.exclude(pub_date__gt=datetime.date(2005,1,3)).exclude(headline='Hello')
用SQL 語句,它等同于:
SELECT...WHERENOTpub_date >'2005-1-3'ANDNOTheadline ='Hello'
注意诱贿,第二個示例更嚴格娃肿。
如果你需要執(zhí)行更復雜的查詢(例如,使用OR 語句查詢)瘪松,你可以使用Q 對象咸作。
annotate
annotate(*args, **kwargs)
使用提供的查詢表達式Annotate 查詢集中的每個對象。查詢表達式可以是一個簡單的值宵睦、模型(或關(guān)聯(lián)模型)字段的一個引用或?qū)Σ樵兗械膶ο笠粋€聚合函數(shù)(平均值记罚、和等)。
New in Django 1.8:
之前版本的Django 值允許聚合函數(shù)用作Annotation】呛浚現(xiàn)在可以使用各種表達式annotate 一個模型桐智。
annotate() 的每個參數(shù)都是一個annotation,它將添加到返回的QuerySet 中每個對象烟馅。
Django 提供的聚合函數(shù)在下文的聚合函數(shù)文檔中講述说庭。
關(guān)鍵字參數(shù)指定的Annotation 將使用關(guān)鍵字作為Annotation 的別名。匿名的參數(shù)的別名將基于聚合函數(shù)的名稱和模型的字段生成郑趁。只有引用單個字段的聚合表達式才可以使用匿名參數(shù)刊驴。其它所有形式都必須用關(guān)鍵字參數(shù)。
例如,如果你正在操作一個Blog 列表捆憎,你可能想知道每個Blog 有多少Entry:
>>>fromdjango.db.modelsimportCount>>>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__count42
Blog 模型本身沒有定義entry__count 屬性舅柜,但是通過使用一個關(guān)鍵字參數(shù)來指定聚合函數(shù),你可以控制Annotation 的名稱:
>>>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_entries42
聚合的深入討論躲惰,參見 聚合主題的指南致份。
order_by
order_by(*fields)
默認情況下,QuerySet 根據(jù)模型Meta 類的ordering 選項排序础拨。你可以使用order_by 方法給每個QuerySet 指定特定的排序氮块。
例如:
Entry.objects.filter(pub_date__year=2005).order_by('-pub_date','headline')
上面的結(jié)果將按照pub_date 降序排序,然后再按照headline 升序排序诡宗。"-pub_date" 前面的負號表示降序排序滔蝉。隱式的是升序排序。若要隨機排序僚焦,請使用"?"锰提,像這樣:
Entry.objects.order_by('?')
注:order_by('?') 查詢可能耗費資源且很慢,這取決于使用的數(shù)據(jù)庫芳悲。
若要按照另外一個模型中的字段排序立肘,可以使用查詢關(guān)聯(lián)模型時的語法。即通過字段的名稱后面跟上兩個下劃線(__)名扛,再跟上新模型中的字段的名稱谅年,直至你希望連接的模型。例如:
Entry.objects.order_by('blog__name','headline')
如果排序的字段與另外一個模型關(guān)聯(lián)肮韧,Django 將使用關(guān)聯(lián)的模型的默認排序融蹂,或者如果沒有指定Meta.ordering 將通過關(guān)聯(lián)的模型的主鍵排序。 例如弄企,因為Blog 模型沒有指定默認的排序:
Entry.objects.order_by('blog')...等同于:Entry.objects.order_by('blog__id')
如果Blog 設(shè)置ordering = ['name']超燃,那么第一個QuerySet 將等同于:
Entry.objects.order_by('blog__name')
通過關(guān)聯(lián)字段排序QuerySet 還能夠不用帶來JOIN 產(chǎn)生的花費,方法是引用關(guān)聯(lián)字段的_id:
# No JoinEntry.objects.order_by('blog_id')# JoinEntry.objects.order_by('blog__id')
New in Django 1.7:
QuerySet 通過關(guān)聯(lián)字段進行排序不用帶來JOIN 產(chǎn)生的開銷拘领。
你還可以通過調(diào)用表達式的asc() 或者desc()意乓,根據(jù)查詢表達式排序:
Entry.objects.order_by(Coalesce('summary','headline').desc())
New in Django 1.8:
增加根據(jù)查詢表達式排序。
如果你還用到distinct()约素,在根據(jù)關(guān)聯(lián)模型中的字段排序時要小心届良。distinct() 中有一個備注講述關(guān)聯(lián)模型的排序如何對結(jié)果產(chǎn)生影響。
注
指定一個多值字段來排序結(jié)果(例如圣猎,一個ManyToManyField 字段或者ForeignKey 字段的反向關(guān)聯(lián))士葫。
考慮下面的情況:
classEvent(Model):parent = models.ForeignKey('self',related_name='children')? ?date = models.DateField()Event.objects.order_by('children__date')
這里,每個Event 可能有多個潛在的排序數(shù)據(jù)送悔;換句話說, 用 order_by()方法對 QuerySet對象進行操作會返回一個擴大版的新QuerySet對象——新增的條目也許并沒有什么卵用慢显,你也用不著它們爪模。
因此,當你使用多值字段對結(jié)果進行排序時要格外小心荚藻。
沒有方法指定排序是否考慮大小寫呻右。對于大小寫的敏感性,Django 將根據(jù)數(shù)據(jù)庫中的排序方式排序結(jié)果鞋喇。
你可以通過Lower將一個字段轉(zhuǎn)換為小寫來排序,它將達到大小寫一致的排序:
Entry.objects.order_by(Lower('headline').desc())
New in Django 1.8:
新增根據(jù)表達式如Lower 來排序眉撵。
如果你不想對查詢做任何排序侦香,即使是默認的排序,可以不帶參數(shù)調(diào)用order_by()纽疟。
你可以通過檢查QuerySet.ordered 屬性來知道查詢是否是排序的罐韩,如果QuerySet 有任何方式的排序它將為True。
每個order_by() 都將清除前面的任何排序污朽。例如散吵,下面的查詢將按照pub_date 排序,而不是headline:
Entry.objects.order_by('headline').order_by('pub_date')
警告
排序不是沒有開銷的操作蟆肆。添加到排序中的每個字段都將帶來數(shù)據(jù)庫的開銷矾睦。添加的每個外鍵也都將隱式包含進它的默認排序。
reverse
reverse()
reverse() 方法反向排序QuerySet 中返回的元素炎功。第二次調(diào)用reverse() 將恢復到原有的排序枚冗。
如要獲取QuerySet 中最后五個元素,你可以這樣做:
my_queryset.reverse()[:5]
注意蛇损,這與Python 中從一個序列的末尾進行切片有點不一樣赁温。上面的例子將首先返回最后一個元素,然后是倒數(shù)第二個元素淤齐,以此類推股囊。如果我們有一個Python 序列,當我們查看seq[-5:] 時更啄,我們將一下子得到倒數(shù)五個元素稚疹。Django 不支持這種訪問模型(從末尾進行切片),因為它不可能利用SQL 高效地實現(xiàn)锈死。
同時還要注意贫堰,reverse() 應(yīng)該只在一個已經(jīng)定義排序的QuerySet 上調(diào)用(例如,在一個定義了默認排序的模型上待牵,或者使用order_by() 的時候)其屏。如果QuerySet 沒有定義排序,調(diào)用reverse() 將不會有任何效果(在調(diào)用reverse() 之前沒有定義排序缨该,那么調(diào)用之后仍保持沒有定義)偎行。
distinct
distinct([*fields])
返回一個在SQL 查詢中使用SELECT DISTINCT 的新QuerySet。它將去除查詢結(jié)果中重復的行。
默認情況下蛤袒,QuerySet 不會去除重復的行熄云。在實際應(yīng)用中,這一般不是個問題妙真,因為像Blog.objects.all() 這樣的簡單查詢不會引入重復的行缴允。但是,如果查詢跨越多張表珍德,當對QuerySet 求值時就可能得到重復的結(jié)果练般。這時候你應(yīng)該使用distinct()。
注
order_by() 調(diào)用中的任何字段都將包含在SQL 的 SELECT 列中锈候。與distinct() 一起使用時可能導致預計不到的結(jié)果薄料。如果你根據(jù)關(guān)聯(lián)模型的字段排序,這些fields將添加到查詢的字段中泵琳,它們可能產(chǎn)生本應(yīng)該是唯一的重復的行摄职。因為多余的列沒有出現(xiàn)在返回的結(jié)果中(它們只是為了支持排序),有時候看上去像是返回了不明確的結(jié)果获列。
示例(除第一個示例外谷市,其他示例都只能在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')[...]
values
values(*fields)
返回一個ValuesQuerySet —— QuerySet 的一個子類,迭代時返回字典而不是模型實例對象蛛倦。
每個字典表示一個對象歌懒,鍵對應(yīng)于模型對象的屬性名稱。
下面的例子將values() 與普通的模型對象進行比較:
# This list contains a Blog object.>>>Blog.objects.filter(name__startswith='Beatles')[]# This list contains a dictionary.>>>Blog.objects.filter(name__startswith='Beatles').values()[{'id':1,'name':'Beatles Blog','tagline':'All the latest Beatles news.'}]
values() 接收可選的位置參數(shù)*fields溯壶,它指定SELECT 應(yīng)該限制哪些字段及皂。如果指定字段,每個字典將只包含指定的字段的鍵/值且改。如果沒有指定字段验烧,每個字典將包含數(shù)據(jù)庫表中所有字段的鍵和值。
例如:
>>>Blog.objects.values()[{'id':1,'name':'Beatles Blog','tagline':'All the latest Beatles news.'}],>>>Blog.objects.values('id','name')[{'id':1,'name':'Beatles Blog'}]
值得注意的幾點:
如果你有一個字段foo 是一個ForeignKey又跛,默認的values() 調(diào)用返回的字典將有一個叫做foo_id 的鍵碍拆,因為這是保存實際的值的那個隱藏的模型屬性的名稱(foo 屬性引用關(guān)聯(lián)的模型)。當你調(diào)用values() 并傳遞字段的名稱慨蓝,傳遞foo 或foo_id 都可以感混,得到的結(jié)果是相同的(字典的鍵會與你傳遞的字段名匹配)。
例如:
>>>Entry.objects.values()[{'blog_id':1,'headline':'First Entry', ...}, ...]>>>Entry.objects.values('blog')[{'blog':1}, ...]>>>Entry.objects.values('blog_id')[{'blog_id':1}, ...]
當values() 與distinct() 一起使用時礼烈,注意排序可能影響最終的結(jié)果弧满。詳細信息參見distinct() 中的備注。
如果values() 子句位于extra() 調(diào)用之后此熬,extra() 中的select 參數(shù)定義的字段必須顯式包含在values() 調(diào)用中庭呜。values() 調(diào)用后面的extra() 調(diào)用將忽略選擇的額外的字段滑进。
在values() 之后調(diào)用only() 和defer() 不太合理,所以將引發(fā)一個NotImplementedError募谎。
New in Django 1.7:
新增最后一點扶关。以前,在values() 之后調(diào)用only() 和defer() 是允許的数冬,但是它要么會崩潰要么返回錯誤的結(jié)果节槐。
ValuesQuerySet 用于你知道你只需要字段的一小部分,而不需要用到模型實例對象的函數(shù)拐纱。只選擇用到的字段當然更高效疯淫。
最后,要注意ValuesQuerySet 是QuerySet 的子類戳玫,它實現(xiàn)了大部分相同的方法。你可以對它調(diào)用filter()未斑、order_by() 等等咕宿。這表示下面的兩個調(diào)用完全相同:
Blog.objects.values().order_by('id')Blog.objects.order_by('id').values()
Django 的作者喜歡將影響SQL 的方法放在前面,然后放置影響輸出的方法(例如values())蜡秽,但是實際上無所謂府阀。這是賣弄你個性的好機會。
你可以通過OneToOneField芽突、ForeignKey 和 ManyToManyField 屬性反向引用關(guān)聯(lián)的模型的字段:
Blog.objects.values('name','entry__headline')[{'name':'My blog','entry__headline':'An entry'},? ? ?{'name':'My blog','entry__headline':'Another entry'},...]
警告
因為ManyToManyField 字段和反向關(guān)聯(lián)可能有多個關(guān)聯(lián)的行试浙,包含它們可能導致結(jié)果集的倍數(shù)放大。如果你在values() 查詢中包含多個這樣的字段將更加明顯寞蚌,這種情況下將返回所有可能的組合田巴。
values_list
values_list(*fields, flat=False)
與values() 類似,只是在迭代時返回的是元組而不是字典挟秤。每個元組包含傳遞給values_list() 調(diào)用的字段的值 —— 所以第一個元素為第一個字段壹哺,以此類推。例如:
>>>Entry.objects.values_list('id','headline')[(1,'First entry'), ...]
如果只傳遞一個字段艘刚,你還可以傳遞flat 參數(shù)管宵。如果為True,它表示返回的結(jié)果為單個值而不是元組攀甚。一個例子會讓它們的區(qū)別更加清晰:
>>>Entry.objects.values_list('id').order_by('id')[(1,), (2,), (3,), ...]>>>Entry.objects.values_list('id', flat=True).order_by('id')[1,2,3, ...]
如果有多個字段箩朴,傳遞flat 將發(fā)生錯誤。
如果你不傳遞任何值給values_list()秋度,它將返回模型中的所有字段炸庞,以它們在模型中定義的順序。
注意静陈,這個方法返回ValuesListQuerySet燕雁。這個類的行為類似列表诞丽。大部分時候它足夠用了,但是如果你需要一個真實的Python 列表對象拐格,可以對它調(diào)用list()僧免,這將會對查詢集求值。
dates
dates(field, kind, order='ASC')
返回一個?DateQuerySet?捏浊,就是提取?QuerySet?查詢中所包含的日期懂衩,將其組成一個新的?datetime.datetime?對象的列表。
field?是你的 model 中的?DateField?或?DateTimeField?字段名稱金踪。
kind?是?"year",?"month"?或?"day"?之一浊洞。 每個?datetime.datetime?對象都會根據(jù)所給的?type?進行截減。
"year"?返回所有時間值中非重復的年分列表胡岔。
"month"?返回所有時間值中非重復的年/月列表法希。
"day"?返回所有時間值中非重復的年/月/日列表。
order, 默認是?'ASC'靶瘸,只有兩個取值?'ASC'?或?'DESC'苫亦。它決定結(jié)果如何排序。
例如:
>>>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','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 就是提取?QuerySet?查詢中所包含的時間怨咪,類似dates屋剑。
none
none()?返回一個?EmptyQuerySet?-- 它是一個運行時只返回空列表的?QuerySet。它經(jīng)常用在這種場合:你要返回一個空列表诗眨,但是調(diào)用者卻需要接收一個?QuerySet?對象唉匾。(這時,就可以用它代替空列表)
Examples:
>>>Entry.objects.none()[]>>>fromdjango.db.models.queryimportEmptyQuerySet>>>isinstance(Entry.objects.none(),EmptyQuerySet)True
all
all()
返回當前QuerySet(或QuerySet 子類) 的副本匠楚。它可以用于在你希望傳遞一個模型管理器或QuerySet 并對結(jié)果做進一步過濾的情況巍膘。不管對哪一種對象調(diào)用all(),你都將獲得一個可以工作的QuerySet芋簿。
當對QuerySet進行求值時典徘,它通常會緩存其結(jié)果。如果數(shù)據(jù)庫中的數(shù)據(jù)在QuerySet求值之后可能已經(jīng)改變益咬,你可以通過在以前求值過的QuerySet上調(diào)用相同的all() 查詢以獲得更新后的結(jié)果逮诲。
select_related
select_related(*fields)
返回一個QuerySet,當執(zhí)行它的查詢時它沿著外鍵關(guān)系查詢關(guān)聯(lián)的對象的數(shù)據(jù)幽告。它會生成一個復雜的查詢并引起性能的損耗梅鹦,但是在以后使用外鍵關(guān)系時將不需要數(shù)據(jù)庫查詢。
下面的例子解釋了普通查詢和select_related() 查詢的區(qū)別冗锁。下面是一個標準的查詢:
# Hits the database.e= Entry.objects.get(id=5)# Hits the database again to get the related Blog object.b= e.blog
下面是一個select_related 查詢:
# Hits the database.e= Entry.objects.select_related('blog').get(id=5)# Doesn't hit the database, because e.blog has been prepopulated# in the previous query.b= e.blog
select_related() 可用于任何對象的查詢集:
fromdjango.utilsimporttimezone# Find all the blogs with entries scheduled to be published in the future.blogs = set()foreinEntry.objects.filter(pub_date__gt=timezone.now()).select_related('blog'):# Without select_related(), this would make a database query for each# loop iteration in order to fetch the related blog for each entry.blogs.add(e.blog)
filter() 和select_related() 鏈的順序不重要齐唆。下面的查詢集是等同的:
Entry.objects.filter(pub_date__gt=timezone.now()).select_related('blog')Entry.objects.select_related('blog').filter(pub_date__gt=timezone.now())
你可以沿著外鍵查詢。如果你有以下模型:
fromdjango.dbimportmodelsclassCity(models.Model):# ...passclassPerson(models.Model):# ...hometown = models.ForeignKey(City)classBook(models.Model):# ...author = models.ForeignKey(Person)
... 那么Book.objects.select_related('author__hometown').get(id=4) 調(diào)用將緩存關(guān)聯(lián)的Person 和關(guān)聯(lián)的 City:
b= Book.objects.select_related('author__hometown').get(id=4)p= b.author# Doesn't hit the database.c = p.hometown# Doesn't hit the database.b= Book.objects.get(id=4)#Noselect_related() in this example.p= b.author# Hits the database.c= p.hometown# Hits the database.
在傳遞給select_related() 的字段中冻河,你可以使用任何ForeignKey 和OneToOneField箍邮。
在傳遞給select_related 的字段中茉帅,你還可以反向引用OneToOneField —— 也就是說,你可以回溯到定義OneToOneField 的字段锭弊。此時堪澎,可以使用關(guān)聯(lián)對象字段的related_name,而不要指定字段的名稱味滞。
有些情況下樱蛤,你希望對很多對象調(diào)用select_related(),或者你不知道所有的關(guān)聯(lián)關(guān)系剑鞍。在這些情況下昨凡,可以調(diào)用不帶參數(shù)的select_related()。它將查找能找到的所有不可為空外鍵 —— 可以為空的外鍵必須明確指定蚁署。大部分情況下不建議這樣做便脊,因為它會使得底層的查詢非常復雜并且返回的很多數(shù)據(jù)都不是真實需要的。
如果你需要清除QuerySet 上以前的select_related 添加的關(guān)聯(lián)字段光戈,可以傳遞一個None 作為參數(shù):
>>>without_relations = queryset.select_related(None)
鏈式調(diào)用select_related 的工作方式與其它方法類似 —— 也就是說就轧,select_related('foo', 'bar') 等同于select_related('foo').select_related('bar')。
Changed in Django 1.7:
在以前田度,后者等同于select_related('bar')。
prefetch_related
對于多對多字段(ManyToManyField)和一對多字段解愤,可以使用prefetch_related()來進行優(yōu)化镇饺。或許你會說送讲,沒有一個叫OneToManyField的東西啊奸笤。實際上 ,F(xiàn)oreignKey就是一個多對一的字段哼鬓,而被ForeignKey關(guān)聯(lián)的字段就是一對多字段了监右。
作用和方法
prefetch_related()和select_related()的設(shè)計目的很相似,都是為了減少SQL查詢的數(shù)量异希,但是實現(xiàn)的方式不一樣健盒。后者是通過JOIN語句,在SQL查詢內(nèi)解決問題称簿。但是對于多對多關(guān)系扣癣,使用SQL語句解決就顯得有些不太明智,因為JOIN得到的表將會很長憨降,會導致SQL語句運行時間的增加和內(nèi)存占用的增加父虑。若有n個對象,每個對象的多對多字段對應(yīng)Mi條授药,就會生成Σ(n)Mi 行的結(jié)果表士嚎。
prefetch_related()的解決方法是呜魄,分別查詢每個表,然后用Python處理他們之間的關(guān)系莱衩。
例如:
fromdjango.dbimportmodelsclassTopping(models.Model):name = models.CharField(max_length=30)classPizza(models.Model):name = models.CharField(max_length=50)? ? toppings = models.ManyToManyField(Topping)def__str__(self):# __unicode__ on Python 2return"%s (%s)"% (self.name,", ".join(topping.namefortoppinginself.toppings.all()))andrun:>>>Pizza.objects.all()["Hawaiian (ham, pineapple)","Seafood (prawns, smoked salmon)"...
extra
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
有些情況下爵嗅,Django的查詢語法難以簡單的表達復雜的 WHERE 子句,對于這種情況, Django 提供了 extra() QuerySet 修改機制 — 它能在 QuerySet生成的SQL從句中注入新子句
Warning
無論何時你都需要非常小心的使用extra().
由于產(chǎn)品差異的原因膳殷,這些自定義的查詢難以保障在不同的數(shù)據(jù)庫之間兼容(因為你手寫 SQL 代碼的原因)操骡,而且違背了 DRY 原則,所以如非必要赚窃,還是盡量避免寫 extra册招。
extra可以指定一個或多個 參數(shù),例如 select, where or tables. 這些參數(shù)都不是必須的,但是你至少要使用一個select勒极。The select 參數(shù)可以讓你在 SELECT 從句中添加其他字段信息是掰,它應(yīng)該是一個字典,存放著屬性名到 SQL 從句的映射辱匿。
Example:
Entry.objects.extra(select={'is_recent':"pub_date > '2006-01-01'"})
結(jié)果集中每個 Entry 對象都有一個額外的屬性is_recent, 它是一個布爾值键痛,表示 Entry對象的pub_date 是否晚于 Jan. 1, 2006.
Django 會直接在 SELECT 中加入對應(yīng)的 SQL 片斷,所以轉(zhuǎn)換后的 SQL 如下:
SELECTblog_entry.*, (pub_date >'2006-01-01')ASis_recentFROMblog_entry;
下面這個例子更復雜一些匾七;它會在每個 Blog對象中添加一個 entry_count 屬性絮短,它會運行一個子查詢,得到相關(guān)聯(lián)的 Entry 對象的數(shù)量:
Blog.objects.extra(select={'entry_count':'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'},)
在上面這個特例中昨忆,我們要了解這個事實丁频,就是 blog_blog 表已經(jīng)存在于FROM從句中
上面的查詢類似下面sql語句:
SELECTblog_blog.*, (SELECTCOUNT(*)FROMblog_entryWHEREblog_entry.blog_id = blog_blog.id)ASentry_countFROMblog_blog;
要注意的是,大多數(shù)數(shù)據(jù)庫需要在子句兩端添加括號邑贴,而在 Django 的select從句中卻無須這樣席里。
例如:
Blog.objects.extra(select=OrderedDict([('a','%s'), ('b','%s')]),? ? select_params=('one','two'))
defer
defer(*fields)
在某些數(shù)據(jù)復雜的環(huán)境下,你的 model 可能包含非常多的字段拢驾,可能某些字段包含非常多的數(shù)據(jù)(比如奖磁,文檔字段),或者將其轉(zhuǎn)化為 Python 對象會消耗非常多的資源繁疤。在這種情況下咖为,有時你可能并不需要這種字段的信息,那么你可以讓 Django 不讀取它們的數(shù)據(jù)稠腊。
將不想載入的字段的名稱傳給?defer()?方法案疲,就可以做到延后載入:
Entry.objects.defer("lede","body")
延后截入字段的查詢返回的仍是 model 類的實例。在你訪問延后載入字段時麻养,你仍可以獲得字段的內(nèi)容褐啡,所不同的是,內(nèi)容是在你訪問延后字段時才讀取數(shù)據(jù)庫的鳖昌,而普通字段是在運行查詢(queryset)時就一次性從數(shù)據(jù)庫中讀取數(shù)據(jù)的备畦。
你可以多次調(diào)用?defer()?方法低飒。每個調(diào)用都可以添加新的延后載入字段:
#Defersboththebodyandledefields.Entry.objects.defer("body").filter(headline="Lennon").defer("lede")
對延后載入字段進行排序是不會起作用的;重復添加延后載入字段也不會有何不良影響懂盐。
你也可以延后載入關(guān)聯(lián) model 中的字段(前提是你使用?select_related()?載入了關(guān)聯(lián) model)褥赊,用法也是用雙下劃線連接關(guān)聯(lián)字段:
Blog.objects.select_related().defer("entry__lede","entry__body")
如果你想清除延后載入的設(shè)置,只要使用將?None?做為參數(shù)傳給?defer()?即可:
# Load all fields immediately.my_queryset.defer(None)
有些字段無論你如何指定莉恼,都不會被延后加載拌喉。比如,你永遠不能延后加載主鍵字段俐银。如果你使用?select_related()?獲得關(guān)聯(lián) model 字段信息尿背,那么你就不能延后載入關(guān)聯(lián) model 的主鍵。(如果這樣做了捶惜,雖然不會拋出錯誤田藐,事實上卻不完成延后加載)
注意
defer()?方法(和隨后提到的?only()?方法) 都只適用于特定情況下的高級屬性。它們可以提供性能上的優(yōu)化吱七,不過前提是你已經(jīng)對你用到的查詢有過很深入細致的分析汽久,非常清楚你需要的究竟是哪些信息,而且已經(jīng)對你所需要的數(shù)據(jù)和默認情況下返回的所有數(shù)據(jù)進行比對踊餐,清楚兩者之間的差異景醇。這完成了上述工作之后,再使用這兩種方法進行優(yōu)化才是有意義的吝岭。所以當你剛開始構(gòu)建你的應(yīng)用時三痰,先不要急著使用?defer()?方法,等你已經(jīng)寫完查詢并且分析成哪些方面是熱點應(yīng)用以后,再用也不遲。
only
only(*fields)
only()?方法或多或少與?defer()?的作用相反奶段。如果你在提取數(shù)據(jù)時希望某個字段不應(yīng)該被延后載入沙兰,而應(yīng)該立即載入,那么你就可以做使用?only()?方法抒钱。如果你一個 model 蜓肆,你希望它所有的字段都延后加載,只有某幾個字段是立即載入的谋币,那么你就應(yīng)該使用?only()?方法仗扬。
如果你有一個 model,它有?name,?age?和?biography?三個字段蕾额,那么下面兩種寫法效果一樣的:
Person.objects.defer("age","biography")Person.objects.only("name")
你無論何時調(diào)用?only()早芭,它都會立刻更改載入設(shè)置。這與它的命名非常相符:只有only?中的字段會立即載入诅蝶,而其他的則都是延后載入的退个。因此募壕,連續(xù)調(diào)用?only()?時,只有最后一個?only?方法才會生效:
# This will defer all fields except the headline.Entry.objects.only("body","lede").only("headline")
由于?defer()?可以遞增(每次都添加字段到延后載入的列表中)语盈,所以你可以將?only()?和?defer()?結(jié)合在一起使用舱馅,請注意使用順序,先only而后defer:
#Finalresultisthat everything except"headline"isdeferred.Entry.objects.only("headline","body").defer("body")#Finalresult loads headline and body immediately (only() replaces any# existingsetof fields).Entry.objects.defer("body").only("headline","body")
using
using(alias)
如果你使用多個數(shù)據(jù)庫刀荒,這個方法用于控制QuerySet 將在哪個數(shù)據(jù)庫上求值代嗤。這個方法的唯一參數(shù)是數(shù)據(jù)庫的別名,定義在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)
返回一個 queryset ?干毅,會鎖定相關(guān)行直到事務(wù)結(jié)束。在支持的數(shù)據(jù)庫上面產(chǎn)生一個SELECT ... FOR UPDATE 語句
例如:
entries= Entry.objects.select_for_update().filter(author=request.user)
所有匹配的行將被鎖定烈炭,直到事務(wù)結(jié)束溶锭。這意味著可以通過鎖防止數(shù)據(jù)被其它事務(wù)修改。
一般情況下如果其他事務(wù)鎖定了相關(guān)行符隙,那么本查詢將被阻塞趴捅,直到鎖被釋放。如果這不是你想要的行為霹疫,請使用select_for_update(nowait=True). 這將使查詢不阻塞拱绑。如果其它事務(wù)持有沖突的鎖, 那么查詢將引發(fā) DatabaseError 異常.
目前 ?postgresql_psycopg2, oracle 和 mysql 數(shù)據(jù)庫后端 select_for_update(). 但是 MySQL 不支持 nowait 參數(shù)。顯然丽蝎,用戶應(yīng)該檢查后端的支持情況猎拨。
當在不支持nowait功能的數(shù)據(jù)庫后端(例如 MySql) 使用nowait=True 參數(shù)調(diào)用 select_for_update() ?時將引發(fā) DatabaseError 異常. 這是防止意外造成代碼被阻塞。
在自動提交模式下使用 select_for_update() 將引發(fā) TransactionManagementError 異常屠阻,原因是自動提交模式下不支持鎖定行红省。如果允許這個調(diào)用,那么可能造成數(shù)據(jù)損壞国觉,而且這個功能很容易在事務(wù)外被調(diào)用吧恃。
對于不支持 SELECT ... FOR UPDATE 的后端 (例如SQLite) ?select_for_update() 將沒有效果。
raw
raw(raw_query, params=None, translations=None)
Changed in Django 1.7:
raw 移動到QuerySet 類中麻诀。以前痕寓,它只位于Manager 中。
接收一個原始的SQL 查詢蝇闭,執(zhí)行它并返回一個django.db.models.query.RawQuerySet 實例呻率。這個RawQuerySet 實例可以迭代以提供實例對象,就像普通的QuerySet 一樣呻引。
更多信息參見執(zhí)行原始的SQL 查詢礼仗。
警告
raw() 永遠觸發(fā)一個新的查詢,而與之前的filter 無關(guān)。因此藐守,它通常應(yīng)該從Manager 或一個全新的QuerySet 實例調(diào)用挪丢。
不會返回QuerySets的方法
這些方法每次被調(diào)用的時候都會查詢數(shù)據(jù)庫。
get
get(**kwargs)
返回按照查詢參數(shù)匹配到的對象卢厂,參數(shù)的格式應(yīng)該符合 Field lookups的要求.
如果匹配到的對象個數(shù)不只一個的話乾蓬,get() 將會觸發(fā)MultipleObjectsReturned 異常. MultipleObjectsReturned 異常是模型類的屬性.
如果根據(jù)給出的參數(shù)匹配不到對象的話,get() 將觸發(fā)DoesNotExist 異常. 這個異常是模型類的屬性. Example:
Entry.objects.get(id='foo')# raises Entry.DoesNotExistThe DoesNotExist exception inheritsfromdjango.core.exceptions.ObjectDoesNotExist, so you can target multiple DoesNotExist exceptions. Example:fromdjango.core.exceptionsimportObjectDoesNotExisttry:? ? e = Entry.objects.get(id=3)? ? b = Blog.objects.get(id=1)exceptObjectDoesNotExist:? ? print("Either the entry or blog doesn't exist.")
create
create(**kwargs)
一個在一步操作中同時創(chuàng)建對象并且保存的便捷方法. 所以:
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")和:p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)
是等同的.
參數(shù) force_insert 在其他的文檔中有介紹, 它意味著一個新的對象一定會被創(chuàng)建. 正常情況中慎恒,你不必要擔心這點. 然而, 如果你的model中有一個你手動設(shè)置主鍵任内, 并且這個值已經(jīng)存在于數(shù)據(jù)庫中, 調(diào)用 create()將會失敗并且觸發(fā) IntegrityError 因為主鍵必須是唯一的. 如果你手動設(shè)置了主鍵,做好異常處理的準備.
get_or_create
get_or_create(defaults=None, **kwargs)
一個通過給出的kwargs 來查詢對象的便捷方法(如果你的模型中的所有字段都有默認值融柬,可以為空)死嗦,需要的話創(chuàng)建一個對象。
返回一個由(object, created)組成的元組粒氧,元組中的object 是一個查詢到的或者是被創(chuàng)建的對象越除, created 是一個表示是否創(chuàng)建了新的對象的布爾值。
這主要用作樣板代碼的一種快捷方式外盯。例如:
try:obj = Person.objects.get(first_name='John', last_name='Lennon')exceptPerson.DoesNotExist:obj = Person(first_name='John', last_name='Lennon', birthday=date(1940,10,9))? ? obj.save()
如果模型的字段數(shù)量較大的話摘盆,這種模式就變的非常不易用了。上面的示例可以用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() 的關(guān)鍵字參數(shù)饱苟,除了一個可選的defaults孩擂,都將傳遞給get() 調(diào)用。如果查找到一個對象箱熬,get_or_create() 返回一個包含匹配到的對象以及False 組成的元組类垦。如果查找到的對象超過一個以上,get_or_create 將引發(fā)MultipleObjectsReturned城须。如果查找不到對象蚤认, get_or_create() 將會實例化并保存一個新的對象,返回一個由新的對象以及True 組成的元組糕伐。新的對象將會大概按照以下的邏輯創(chuàng)建:
params= {k: vfork, vinkwargs.items()if'__'notink}params.update(defaults)obj = self.model(**params)obj.save()
它表示從非'defaults' 且不包含雙下劃線的關(guān)鍵字參數(shù)開始(暗示這是一個不精確的查詢)砰琢。然后將defaults 的內(nèi)容添加進來,覆蓋必要的鍵赤炒,并使用結(jié)果作為關(guān)鍵字參數(shù)傳遞給模型類氯析。這是對用到的算法的簡單描述亏较,但它包含了所有的相關(guān)的細節(jié)莺褒。內(nèi)部的實現(xiàn)有更多的錯誤檢查并處理一些邊緣條件;如果感興趣雪情,請閱讀代碼遵岩。
如果你有一個名為defaults的字段,并且想在get_or_create() 是用它作為精確查詢,只需要使用'defaults__exact'尘执,像這樣:
Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults':'baz'})
當你使用手動指定的主鍵時舍哄,get_or_create() 方法與create()方法有相似的錯誤行為 。如果需要創(chuàng)建一個對象而該對象的主鍵早已存在于數(shù)據(jù)庫中誊锭,IntegrityError 異常將會被觸發(fā)表悬。
這個方法假設(shè)正確使用原子操作,正確的數(shù)據(jù)庫配置和底層數(shù)據(jù)庫的正確行為丧靡。然而蟆沫,如果數(shù)據(jù)庫級別沒有對get_or_create 中用到的kwargs 強制要求唯一性(參見unique 和 unique_together),這個方法容易導致競態(tài)條件可能會仍具有相同參數(shù)的多行同時插入温治。
如果你正在使用MySQL饭庞,請確保使用READ COMMITTED 隔離級別而不是默認的REPEATABLE READ,否則你將會遇到get_or_create 引發(fā)IntegrityError 但對象在接下來的get() 調(diào)用中并不存在的情況熬荆。
最后講一句get_or_create() 在Django 視圖中的使用舟山。請確保只在POST 請求中使用,除非你有充分的理由卤恳。GET 請求不應(yīng)該對數(shù)據(jù)有任何影響累盗。而POST 則用于對數(shù)據(jù)產(chǎn)生影響的請求。更多信息纬黎,參見HTTP 細則中的安全的方法幅骄。
警告
你可以通過ManyToManyField 屬性和反向關(guān)聯(lián)使用get_or_create()。在這種情況下本今,你應(yīng)該限制查詢在關(guān)聯(lián)的上下文內(nèi)部拆座。如果你不一致地使用它,將可能導致完整性問題冠息。
根據(jù)下面的模型:
classChapter(models.Model):title = models.CharField(max_length=255,unique=True)classBook(models.Model):title = models.CharField(max_length=256)? ? chapters = models.ManyToManyField(Chapter)
你可以通過Book 的chapters 字段使用get_or_create()挪凑,但是它只會獲取該Book 內(nèi)部的上下文:
>>>book = Book.objects.create(title="Ulysses")>>>book.chapters.get_or_create(title="Telemachus")(,True)>>>book.chapters.get_or_create(title="Telemachus")(,False)>>>Chapter.objects.create(title="Chapter 1")>>>book.chapters.get_or_create(title="Chapter 1")# Raises IntegrityError
發(fā)生這個錯誤時因為它嘗試通過Book “Ulysses” 獲取或者創(chuàng)建“Chapter 1”,但是它不能:關(guān)聯(lián)關(guān)系不能獲取這個chapter 因為它與這個book 不關(guān)聯(lián)逛艰,但因為title 字段是唯一的它仍然不能創(chuàng)建躏碳。
update_or_create
update_or_create(defaults=None, **kwargs)
New in Django 1.7.
一個通過給出的kwargs 來更新對象的便捷方法, 如果需要的話創(chuàng)建一個新的對象散怖。defaults 是一個由 (field, value) 對組成的字典菇绵,用于更新對象。
返回一個由 (object, created)組成的元組,元組中的object 是一個創(chuàng)建的或者是被更新的對象镇眷, created 是一個標示是否創(chuàng)建了新的對象的布爾值咬最。
update_or_create 方法嘗試通過給出的kwargs 去從數(shù)據(jù)庫中獲取匹配的對象。如果找到匹配的對象欠动,它將會依據(jù)defaults 字典給出的值更新字段永乌。
這用作樣板代碼的一種快捷方式惑申。例如:
try:? ? obj = Person.objects.get(first_name='John', last_name='Lennon')forkey,valueinupdated_values.iteritems():? ? ? ? setattr(obj, key,value)? ? obj.save()exceptPerson.DoesNotExist:? ? updated_values.update({'first_name':'John','last_name':'Lennon'})? ? obj = Person(**updated_values)? ? obj.save()
如果模型的字段數(shù)量較大的話,這種模式就變的非常不易用翅雏。上面的示例可以用 update_or_create() 重寫:
obj, created = Person.objects.update_or_create(? ? first_name='John', last_name='Lennon', defaults=updated_values)
kwargs 中的名稱如何解析的詳細描述可以參見get_or_create()圈驼。
和上文描述的get_or_create() 一樣,這個方式容易導致競態(tài)條件望几,如果數(shù)據(jù)庫層級沒有前置唯一性它會讓多行同時插入绩脆。
bulk_create
bulk_create(objs, batch_size=None)
使用django orm大批量插入的時候我們可以不使用for循環(huán)對一個一個的save而是使用
bulk_create來批量插入
例如:
>>>Entry.objects.bulk_create([...Entry(headline="Django 1.0 Released"),...Entry(headline="Django 1.1 Announced"),...Entry(headline="Breaking: Django is awesome")...])
count
count()
返回在數(shù)據(jù)庫中對應(yīng)的 QuerySet.對象的個數(shù)。 count() 永遠不會引發(fā)異常橄抹。
Example:
# 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()
in_bulk
接收一個主鍵值列表衙伶,然后根據(jù)每個主鍵值所其對應(yīng)的對象,返回一個主鍵值與對象的映射字典害碾。
例如:
>>>Blog.objects.in_bulk([1]){1: }>>>Blog.objects.in_bulk([1,2]){1: ,2: }>>>Blog.objects.in_bulk([]){}
如果你給?in_bulk()?傳遞的是一個空列表明矢劲,得到就是一個空字典。
iterator
iterator()
運行查詢(QuerySet)慌随,然后根據(jù)結(jié)果返回一個?迭代器(iterator芬沉。 做為比較,使用?QuerySet?時阁猜,從數(shù)據(jù)庫中讀取所有記錄后丸逸,一次性將所有記錄實例化為對應(yīng)的對象;而?iterator()?則是讀取記錄后剃袍,是分多次對數(shù)據(jù)實例化黄刚,用到哪個對象才實例化哪個對象。相對于一次性返回很多對象的?QuerySet民效,使用迭代器不僅效率更高憔维,而且更節(jié)省內(nèi)存。
要注意的是畏邢,如果將?iterator()?作用于?QuerySet业扒,那就意味著會再一次運行查詢,就是說會運行兩次查詢舒萎。
latest
latest(field_name=None)
根據(jù)時間字段?field_name?得到最新的對象程储。
下面這個例子根據(jù)?pub_date?字段得到數(shù)據(jù)表中最新的?Entry?對象:
Entry.objects.latest('pub_date')
如果你在 model 中?Meta?定義了?get_latest_by?項, 那么你可以略去?field_name?參數(shù)。Django 會將?get_latest_by?做為默認設(shè)置臂寝。
和?get(),?latest()?一樣章鲤,如果根據(jù)所給條件沒有找到匹配的對象,就會拋出?DoesNotExist?異常咆贬。
注意?latest()?是純粹為了易用易讀而存在的方法败徊。
earliest
earliest(field_name=None)
一種類似latest()的查詢
first
first()
返回結(jié)果集的第一個對象, 當沒有找到時返回None.如果 QuerySet 沒有設(shè)置排序,則將會自動按主鍵進行排序
Example:
p= Article.objects.order_by('title','pub_date').first()
筆記:first() 是一個簡便方法 下面這個例子和上面的代碼效果是一樣
try:? ? p = Article.objects.order_by('title','pub_date')[0]exceptIndexError:? ? p =None
last
last()
工作方式類似first(),只是返回的是查詢集中最后一個對象素征。
aggregate
aggregate(*args, **kwargs)
返回一個字典集嵌,包含根據(jù)QuerySet 計算得到的聚合值(平均數(shù)、和等等)御毅。aggregate() 的每個參數(shù)指定返回的字典中將要包含的值根欧。
Django 提供的聚合函數(shù)在下文的聚合函數(shù)中講述。因為聚合也是查詢表達式端蛆,你可以組合多個聚合以及值來創(chuàng)建復雜的聚合凤粗。
使用關(guān)鍵字參數(shù)指定的聚合將使用關(guān)鍵字參數(shù)的名稱作為Annotation 的名稱。匿名的參數(shù)的名稱將基于聚合函數(shù)的名稱和模型字段生成今豆。復雜的聚合不可以使用匿名參數(shù)嫌拣,它們必須指定一個關(guān)鍵字參數(shù)作為別名。
例如呆躲,當你使用Blog Entry 時异逐,你可能想知道對Author 貢獻的Blog Entry 的數(shù)目:
>>>fromdjango.db.modelsimportCount>>>q = Blog.objects.aggregate(Count('entry')){'entry__count':16}
通過使用關(guān)鍵字參數(shù)來指定聚合函數(shù),你可以控制返回的聚合的值的名稱:
>>>q = Blog.objects.aggregate(number_of_entries=Count('entry')){'number_of_entries':16}
聚合的深入討論插掂,參見聚合的指南灰瞻。
exists
exists()
如果QuerySet 包含任何結(jié)果,則返回True辅甥,否則返回False酝润。它會試圖用最簡單和最快的方法完成查詢,但它執(zhí)行的方法與普通的QuerySet 查詢確實幾乎相同璃弄。
exists() 用于搜尋對象是否在QuerySet 中以及QuerySet 是否存在任何對象要销,特別是QuerySet 比較大的時候。
查找具有唯一性字段(例如primary_key)的模型是否在一個QuerySet 中的最高效的方法是:
entry =Entry.objects.get(pk=123)ifsome_queryset.filter(pk=entry.pk).exists():print("Entry contained in queryset")
它將比下面的方法快很多夏块,這個方法要求對QuerySet 求值并迭代整個QuerySet:
ifentryinsome_queryset:print("Entry contained in QuerySet")
若要查找一個QuerySet 是否包含任何元素:
ifsome_queryset.exists():print("There is at least one object in some_queryset")
將快于:
ifsome_queryset:print("There is at least one object in some_queryset")
... 但不會快很多(因為這需要很大的QuerySet 以獲得效率的提升)疏咐。
另外,如果some_queryset 還沒有求值脐供,但你知道它將在某個時刻求值凳鬓,那么使用some_queryset.exists() 將比簡單地使用bool(some_queryset) 完成更多的工作(一個查詢用于存在性檢查,另外一個是后面的求值)患民,后者將求值并檢查是否有結(jié)果返回缩举。
update
update(**kwargs)
更新操作
例如:
>>>Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
更新特定的數(shù)據(jù)
>>>Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')
不能對外鍵進行更新操作
>>>Entry.objects.update(blog__name='foo')# Won't work!
可以通過外鍵進行更新
>>>Entry.objects.filter(blog__id=1).update(comments_on=True)
下面是更新的變種方法
e = Entry.objects.get(id=10)e.comments_on =Falsee.save()
上面的語句類似下面的語句:
Entry.objects.filter(id=10).update(comments_on=False)
delete
delete()
刪除操作
例如:
>>>b = Blog.objects.get(pk=1)
刪除所有滿足條件的
>>>Entry.objects.filter(blog=b).delete()
上面操作類似于下面語句:
blogs= Blog.objects.all()# This will delete all Blogs and all of their Entry objects.blogs.delete()
as_manager
classmethod as_manager()
New in Django 1.7.
類方法返回一個管理器實例。
字段查找
字段查詢是指如何指定SQL WHERE子句的內(nèi)容. 它們通過查詢集的filter(), exclude() and get()的關(guān)鍵字參數(shù)指定.
exact
精確匹配匹颤。
Examples:
Entry.objects.get(id__exact=14)Entry.objects.get(id__exact=None)SQL equivalents:SELECT...WHEREid=14;SELECT...WHEREidISNULL;
iexact
不區(qū)分大小寫的精確匹配
例如:
Blog.objects.get(name__iexact='beatles blog')Blog.objects.get(name__iexact=None)SQL equivalents:SELECT...WHEREnameILIKE'beatles blog';SELECT...WHEREnameISNULL;
contains
模糊匹配.
例如:
Entry.objects.get(headline__contains='Lennon')SQL equivalent:SELECT...WHEREheadlineLIKE'%Lennon%';
icontains
不區(qū)分大小寫的模糊匹配
例如:
Entry.objects.get(headline__icontains='Lennon')SQL equivalent:SELECT...WHEREheadlineILIKE'%Lennon%';
in
相當于數(shù)據(jù)庫的in查詢.
例如:
Entry.objects.filter(id__in=[1,3,4])SQL equivalent:SELECT...WHEREidIN(1,3,4);
你也可以動態(tài)生成list進行in查詢:
inner_qs= Blog.objects.filter(name__contains='Cheddar')entries= Entry.objects.filter(blog__in=inner_qs)
上面的語句類似于下面的sql語句:
SELECT...WHEREblog.idIN(SELECTidFROM...WHERENAMELIKE'%Cheddar%')
如果使用ValuesQuerySet 查詢仅孩,確保只選擇一個列:
inner_qs= Blog.objects.filter(name__contains='Ch').values('name')entries= Entry.objects.filter(blog__name__in=inner_qs)
這個例子將產(chǎn)生一個異常,由于內(nèi)查詢試圖提取兩個字段的值印蓖,但是查詢語句只期望提取一個字段的值:
# 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)
gt
大于
例子:
Entry.objects.filter(id__gt=4)SQL語句相當于:SELECT...WHEREid>4;
gte
大于或等于
lt
小于
lte
小于或等于
startswith
區(qū)分大小寫辽慕,開始位置匹配
例如:
Entry.objects.filter(headline__startswith='Will')SQL equivalent:SELECT...WHEREheadlineLIKE'Will%';
SQLite 不支持區(qū)分大小寫 LIKE 語句; Sqlite 下startswith 等于 istartswith .
istartswith
不區(qū)分大小寫,開始位置匹配
例如:
Entry.objects.filter(headline__istartswith='will')SQL equivalent:SELECT...WHEREheadlineILIKE'Will%';
endswith
以什么結(jié)尾.
例如:
Entry.objects.filter(headline__endswith='cats')SQL equivalent:SELECT...WHEREheadlineLIKE'%cats';
iendswith
以什么結(jié)尾不區(qū)分大小寫.
例如:
Entry.objects.filter(headline__iendswith='will')SQL equivalent:SELECT...WHEREheadlineILIKE'%will'
range
范圍查詢赦肃,類似于sql的between and.
Example:
importdatetimestart_date = datetime.date(2005,1,1)end_date = datetime.date(2005,3,31)Entry.objects.filter(pub_date__range=(start_date, end_date))SQL equivalent:SELECT...WHEREpub_dateBETWEEN'2005-01-01'and'2005-03-31';
year
對日期/時間字段精確匹配年分溅蛉,年分用四位數(shù)字表示公浪。
例如:
Entry.objects.filter(pub_date__year=2005)
等價于 SQL:
SELECT...WHEREEXTRACT('year'FROMpub_date) ='2005';
(不同的數(shù)據(jù)庫引擎中,翻譯得到的 SQL 也不盡相同船侧。)
month
對日期/時間字段精確匹配月分欠气,用整數(shù)表示月分,比如 1 表示一月镜撩,12 表示十二月预柒。
例如:
Entry.objects.filter(pub_date__month=12)
等價于 SQL:
SELECT...WHEREEXTRACT('month'FROMpub_date) ='12';
(不同的數(shù)據(jù)庫引擎中,翻譯得到的 SQL 也不盡相同袁梗。)
day
對于日期和日期時間字段宜鸯,具體到某一天的匹配。取一個整數(shù)的天數(shù)遮怜。
Example:
Entry.objects.filter(pub_date__day=3)SQL equivalent:SELECT...WHEREEXTRACT('day'FROMpub_date) ='3';
week_day
對日期/時間字段匹配星期幾
例如:
Entry.objects.filter(pub_date__week_day=2)
等價于 SQL:
SELECT...WHEREEXTRACT('dow'FROMpub_date) ='2';
(不同的數(shù)據(jù)庫引擎中淋袖,翻譯得到的 SQL 也不盡相同。)
要注意的是锯梁,這段代碼將得到 pub_date 字段是星期一的所有記錄 (西方習慣于將星期一看做一周的第二天)适贸,與它的年月信息無關(guān)。星期以星期天做為第一天涝桅,以星期六做為最后一天拜姿。
hour
對于小時的匹配,取值只能是0到23之間.
例如:
Event.objects.filter(timestamp__hour=23)SQL equivalent:SELECT...WHEREEXTRACT('hour'FROMtimestamp) ='23';
minute
對于分鐘的匹配冯遂,取值只能是0到59之間.
例如:
Event.objects.filter(timestamp__minute=29)SQL equivalent:SELECT...WHEREEXTRACT('minute'FROMtimestamp) ='29';
second
對于秒的匹配蕊肥,取值只能是0到59之間.
例如:
Event.objects.filter(timestamp__second=31)等同于SQL語句:SELECT...WHEREEXTRACT('second'FROMtimestamp) ='31';
isnull
值為 True 或 False, 相當于 SQL語句IS NULL和IS NOT NULL.
例如:
Entry.objects.filter(pub_date__isnull=True)SQL equivalent:SELECT...WHEREpub_dateISNULL;
search
一個Boolean類型的全文搜索,以全文搜索的優(yōu)勢蛤肌。這個很像 contains 壁却,但是由于全文索引的優(yōu)勢,以使它更顯著的快裸准。
Example:
Entry.objects.filter(headline__search="+Django -jazz Python")SQL equivalent:SELECT...WHEREMATCH(tablename, headline) AGAINST (+Django -jazz PythonINBOOLEANMODE);
regex
正則表達式的匹配規(guī)則
例如:
Entry.objects.get(title__regex=r'^(An?|The) +')SQL equivalents:SELECT...WHEREtitle REGEXPBINARY'^(An?|The) +';-- MySQLSELECT...WHEREREGEXP_LIKE(title,'^(an?|the) +','c');-- OracleSELECT...WHEREtitle ~'^(An?|The) +';-- PostgreSQLSELECT...WHEREtitle REGEXP'^(An?|The) +';-- SQLite
iregex
正則表達式的匹配規(guī)則展东,忽略大小寫
例如:
Entry.objects.get(title__iregex=r'^(an?|the) +')SQL equivalents:SELECT...WHEREtitle REGEXP'^(an?|the) +';-- MySQLSELECT...WHEREREGEXP_LIKE(title,'^(an?|the) +','i');-- OracleSELECT...WHEREtitle ~*'^(an?|the) +';-- PostgreSQLSELECT...WHEREtitle REGEXP'(?i)^(an?|the) +';-- SQLite
聚合函數(shù)
Django 的django.db.models 模塊提供以下聚合函數(shù)。關(guān)于如何使用這些聚合函數(shù)的細節(jié)炒俱,參見聚合函數(shù)的指南盐肃。關(guān)于如何創(chuàng)建聚合函數(shù),參數(shù)聚合函數(shù) 的文檔权悟。
警告
SQLite 不能直接處理日期/時間字段的聚合砸王。這是因為SQLite 中沒有原生的日期/時間字段,Django 目前使用文本字段模擬它的功能峦阁。在SQLite 中對日期/時間字段使用聚合將引發(fā)NotImplementedError谦铃。
注
在QuerySet 為空時,聚合函數(shù)函數(shù)將返回None榔昔。 例如驹闰,如果QuerySet 中沒有記錄瘪菌,Sum 聚合函數(shù)將返回None 而不是0。Count 是一個例外嘹朗,如果QuerySet 為空师妙,它將返回0。
所有聚合函數(shù)具有以下共同的參數(shù):
expression
引用模型字段的一個字符串骡显,或者一個查詢表達式。
New in Django 1.8:
現(xiàn)在在復雜的計算中曾掂,聚合函數(shù)可以引用多個字段惫谤。
output_field
用來表示返回值的模型字段,它是一個可選的參數(shù)珠洗。
New in Django 1.8:
添加output_field 參數(shù)溜歪。
注
在組合多個類型的字段時,只有在所有的字段都是相同類型的情況下许蓖,Django 才能確定output_field蝴猪。否則,你必須自己提供output_field 參數(shù)膊爪。
**extra
這些關(guān)鍵字參數(shù)可以給聚合函數(shù)生成的SQL 提供額外的信息自阱。
Avg
class Avg(expression, output_field=None, **extra)
返回給定expression 的平均值,其中expression 必須為數(shù)值米酬。
默認的別名:__avg
返回類型:float
Count
class Count(expression, distinct=False, **extra)
返回與expression 相關(guān)的對象的個數(shù)沛豌。
默認的別名:__count
返回類型:int
有一個可選的參數(shù):distinct?如果distinct=True,Count 將只計算唯一的實例赃额。它等同于COUNT(DISTINCT ) SQL 語句加派。默認值為False。
Max
class Max(expression, output_field=None, **extra)
返回expression 的最大值跳芳。
默認的別名:__max
返回類型:與輸入字段的類型相同芍锦,如果提供則為 output_field 類型
Min
class Min(expression, output_field=None, **extra)
返回expression 的最小值。
默認的別名:__min
返回的類型:與輸入字段的類型相同飞盆,如果提供則為 output_field 類型
StdDev
class StdDev(expression, sample=False, **extra)
返回expression 的標準差娄琉。
默認的別名:__stddev
返回類型:float
有一個可選的參數(shù):
sample
默認情況下,StdDev 返回群體的標準差吓歇。但是车胡,如果sample=True,返回的值將是樣本的標準差照瘾。
SQLite
SQLite 沒有直接提供StdDev匈棘。有一個可用的實現(xiàn)是SQLite 的一個擴展模塊。參見SQlite 的文檔 中獲取并安裝這個擴展的指南析命。
Sum
class Sum(expression, output_field=None, **extra)
計算expression 的所有值的和主卫。
默認的別名:__sum
返回類型:與輸入的字段相同逃默,如果提供則為output_field 的類型
Variance
class Variance(expression, sample=False, **extra)
返回expression 的方差。
默認的別名:__variance
返回的類型:float
有一個可選的參數(shù):sample?默認情況下簇搅,Variance 返回群體的方差完域。但是,如果sample=True瘩将,返回的值將是樣本的方差吟税。
SQLite
SQLite 沒有直接提供Variance。有一個可用的實現(xiàn)是SQLite 的一個擴展模塊姿现。 參見SQlite 的文檔 中獲取并安裝這個擴展的指南肠仪。
查詢相關(guān)的類
本節(jié)提供查詢相關(guān)的工具的參考資料,它們其它地方?jīng)]有文檔备典。
Q() 對象
class Q
Q對象(django.db.models.Q)可以對關(guān)鍵字參數(shù)進行封裝异旧,從而更好地應(yīng)用多個查詢。
一般我們在Django程序中查詢數(shù)據(jù)庫操作都是在QuerySet里進行進行提佣,例如下面代碼:
>>>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())
或者將其組合起來吮蛹,例如:
>>>q1 = Entry.objects.filter(headline_startswith="What").exclude(pub_date_gte=datetime.date.today())
隨著我們的程序越來越復雜,查詢的條件也跟著復雜起來拌屏,這樣簡單的通過一個filter()來進行查詢的條件將導致我們的查詢越來越長潮针。
Q()對象就是為了將這些條件組合起來。
當我們在查詢的條件中需要組合條件時(例如兩個條件“且”或者“或”)時倚喂。我們可以使用Q()查詢對象然低。例如下面的代碼
fromdjango.db.modelsimports Qq=Q(question_startswith="What")
這樣就生成了一個Q()對象,我們可以使用符號&或者|將多個Q()對象組合起來傳遞給filter()务唐,exclude()雳攘,get()等函數(shù)。當多個Q()對象組合起來時枫笛,Django會自動生成一個新的Q()吨灭。例如下面代碼就將兩個條件組合成了一個
Q(question__startswith='Who') | Q(question__startswith='What')
使用上述代碼可以使用SQL語句這么理解:
WHEREquestionLIKE'Who%'ORquestionLIKE'What%'
我們可以在Q()對象的前面使用字符“~”來代表意義“非”,例如下面代碼:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
對應(yīng)SQL語句可以理解為:
WHEREquestionlike"Who%"ORyear(pub_date) !=2005
這樣我們可以使用 “&”或者“|”還有括號來對條件進行分組從而組合成更加復雜的查詢邏輯刑巧。
也可以傳遞多個Q()對象給查詢函數(shù)喧兄,例如下面代碼:
News.objects,get(? ? Q(question__startswith='Who'),? ? Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)))
多個Q()對象之間的關(guān)系Django會自動理解成“且(and)”關(guān)系。如上面代碼使用SQL語句理解將會是:
SELECT* fromnewsWHEREquestionLIKE'Who%'AND(pub_date ='2005-05-02'ORpub_date ='2005-05-06')
Q()對象可以結(jié)合關(guān)鍵字參數(shù)一起傳遞給查詢函數(shù)啊楚,不過需要注意的是要將Q()對象放在關(guān)鍵字參數(shù)的前面吠冤,看下面代碼
#正確的做法
News.objects.get(? ? Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)),? ? question__startswith='Who')
#錯誤的做法,代碼將關(guān)鍵字參數(shù)放在了Q()對象的前面恭理。
News.objects.get(? ? question__startswith='Who',? ? Q(pub_date=date(2005,5,2)) | Q(pub_date=date(2005,5,6)))