Django查詢集 API

原文: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)))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拯辙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涯保,老刑警劉巖诉濒,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異夕春,居然都是意外死亡未荒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門及志,熙熙樓的掌柜王于貴愁眉苦臉地迎上來片排,“玉大人,你說我怎么就攤上這事速侈÷使眩” “怎么了?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵锌畸,是天一觀的道長勇劣。 經(jīng)常有香客問我靖避,道長潭枣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任幻捏,我火速辦了婚禮盆犁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篡九。我一直安慰自己谐岁,他們只是感情好,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布榛臼。 她就那樣靜靜地躺著伊佃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沛善。 梳的紋絲不亂的頭發(fā)上航揉,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機與錄音金刁,去河邊找鬼帅涂。 笑死,一個胖子當著我的面吹牛尤蛮,可吹牛的內(nèi)容都是我干的媳友。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼产捞,長吁一口氣:“原來是場噩夢啊……” “哼醇锚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坯临,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤搂抒,失蹤者是張志新(化名)和其女友劉穎艇搀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體求晶,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡焰雕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了芳杏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矩屁。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖爵赵,靈堂內(nèi)的尸體忽然破棺而出吝秕,到底是詐尸還是另有隱情,我是刑警寧澤空幻,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布烁峭,位于F島的核電站,受9級特大地震影響秕铛,放射性物質(zhì)發(fā)生泄漏约郁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一但两、第九天 我趴在偏房一處隱蔽的房頂上張望鬓梅。 院中可真熱鬧,春花似錦谨湘、人聲如沸绽快。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坊罢。三九已至,卻和暖如春擅耽,著一層夾襖步出監(jiān)牢的瞬間活孩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工秫筏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留诱鞠,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓这敬,卻偏偏與公主長得像航夺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子崔涂,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容