介紹Python的Django框架中的QuerySets
Django的QuerySets酷斃了!
在本文中我將解釋一下QuerySets是什么,它是如何工作的(如果你對(duì)它已經(jīng)熟悉了,你可以直接跳到第二部分),我認(rèn)為如果可以的話你應(yīng)該總是返回QuerySets對(duì)象,下面讓我來(lái)談?wù)勅绾巫觥?br /> QuerySets很酷
QuerySet,本質(zhì)上是一個(gè)給定的模型的對(duì)象列表。我說(shuō)“列表”而不是“組”或更正式的“集合”因?yàn)樗怯行虻摹J聦?shí)上,你可能已經(jīng)熟悉如何獲得QuerySets,因?yàn)檫@就是你調(diào)用variousBook.objects.XXX()方法后得到的對(duì)象。例如,考慮下面的語(yǔ)句:
Book.objects.all()
all()返回的就是Book實(shí)例的一個(gè)QuerySet,它正好包括allBookinstances,下面的其他調(diào)用你可能已經(jīng)知道:
# Return all books published since 1990 Book.objects.filter(year_published__gt=1990) # Return all books *not* written by Richard Dawkins Book.objects.exclude(author='Richard Dawkins') # Return all books, ordered by author name, then # chronologically, with the newer ones first. Book.objects.order_by('author', '-year_published')
關(guān)于 QuerySet s最酷的是,由于這些函數(shù)操作、返回的都是一個(gè)QuerySet,你可以把他們鏈起來(lái):
# Return all book published after 1990, except for # ones written by Richard Dawkins. Order them by # author name, then chronologically, with the newer # ones first. Book.objects.filter(year_published__gt=1990) \ .exclude(author='Richard Dawkins') \ .order_by('author', '-year_published')
而且這并不是全部的,它更快!
在內(nèi)部,一個(gè)QuerySet可以被構(gòu)造、過(guò)濾、切片及像普通變量那樣在沒(méi)有實(shí)際數(shù)據(jù)庫(kù)查詢的情況下隨便傳遞,在評(píng)估處理完QuerySet前不產(chǎn)生數(shù)據(jù)庫(kù)活動(dòng)。
所有我們確認(rèn)了QuerySets很酷,不是么?
盡可能的返回QuerySets
我最近曾在一個(gè)Django應(yīng)用中用一個(gè)模型來(lái)表示樹(數(shù)據(jù)結(jié)構(gòu),不是圣誕裝飾)。這意味著每一個(gè)實(shí)例在樹上都有一個(gè)指向它父節(jié)點(diǎn)的鏈接。它看起來(lái)像這樣:
class Node(models.Model): parent = models.ForeignKey(to='self', null=True, blank=True) value = models.IntegerField() def __unicode__(self): return 'Node #{}'.format(self.id) def get_ancestors(self): if self.parent is None: return [] return [self.parent] + self.parent.get_ancestors()
這工作的相當(dāng)好。麻煩的是,我不得不添加另一種方法,get_larger_ancestors,它應(yīng)該返回所有值大于當(dāng)前節(jié)點(diǎn)的的父節(jié)點(diǎn)。這是我能實(shí)現(xiàn)這個(gè):
def get_larger_ancestors(self): ancestors = self.get_ancestors() return [node for node in ancestors if node.value > self.value]
問(wèn)題是,我基本上會(huì)在名單上審查兩次——Django一次,我自己一次。這讓我考慮到-如果get_ancestors返回QuerySet而不是列表會(huì)怎樣呢?我可以這樣做:
def get_larger_ancestors(self): return self.get_ancestors().filter(value__gt=self.value)
很簡(jiǎn)單,這里更重要的是我沒(méi)有遍歷對(duì)象。我可以對(duì)get_larger_ancestors的返回使用任何我想使用的過(guò)濾器,而且感到安全——我不會(huì)得到一個(gè)未知大小的對(duì)象列表。這樣的主要優(yōu)勢(shì)是我一直使用相同的查詢接口。當(dāng)用戶得到了一大堆的對(duì)象,我們不知道他想怎樣對(duì)它們進(jìn)行切片分塊。而返回QuerySet對(duì)象時(shí)我保證用戶知道如何處理它。
但如何實(shí)現(xiàn)get_ancestorsto返回一個(gè)QuerySet呢?這是一個(gè)小技巧。用一條簡(jiǎn)單的查詢收集我們需要的數(shù)據(jù)是不可能的,使用任何預(yù)定數(shù)量的查詢也是不可能的。我們要找的法則是動(dòng)態(tài)的,選擇的實(shí)現(xiàn)看起來(lái)很像它現(xiàn)在的樣子,下面就是選擇,一個(gè)更好的實(shí)現(xiàn):
class Node(models.Model): parent = models.ForeignKey(to='self', null=True, blank=True) value = models.IntegerField() def __unicode__(self): return 'Node #{}'.format(self.id) def get_ancestors(self): if self.parent is None: return Node.objects.none() return Node.objects.filter(pk=self.parent.pk) | self.parent.get_ancestors() def get_larger_ancestors(self): return self.get_ancestors().filter(value__gt=self.value)
稍停一會(huì),沉淀一下,馬上說(shuō)出細(xì)節(jié)。
我想說(shuō)的是,不論什么時(shí)候你返回一系列對(duì)象——你應(yīng)該總是返回一個(gè)QuerySet替代。這樣做將允許用戶使用一種簡(jiǎn)單、熟悉、具備更好性能的方法自由過(guò)濾、剪接和排序結(jié)果。
(從一個(gè)側(cè)面說(shuō)get_ancestors查詢了數(shù)據(jù)庫(kù),因?yàn)槲沂褂昧诉f歸的self.parent。這里有一個(gè)額外的數(shù)據(jù)庫(kù)執(zhí)行——當(dāng)實(shí)際檢測(cè)結(jié)果時(shí)執(zhí)行了這個(gè)函數(shù),未來(lái)又執(zhí)行了另外一次。當(dāng)我們?cè)跀?shù)據(jù)庫(kù)查詢上使用更多的過(guò)濾器或進(jìn)行高耗內(nèi)存的操作時(shí)我們得到了性能的提升。這里的例子
常見的QuerySet操作
所以,執(zhí)行簡(jiǎn)單查詢時(shí)返回一個(gè)QuerySet很簡(jiǎn)單。當(dāng)我們想實(shí)現(xiàn)復(fù)雜一點(diǎn)的東西,我們需要執(zhí)行相關(guān)操作(也包括一些助手函數(shù))。下面是些小竅門(作為練習(xí),試著理解我get_larger_ancestors的實(shí)現(xiàn))。
- 聯(lián)合 - QuerySet的聯(lián)合運(yùn)算符是|,處理復(fù)制時(shí)管道“symbol.qs1 | qs2”返回所有來(lái)自qs1和qs2項(xiàng)目的QuerySet(都在QuerySet的項(xiàng)目將只在結(jié)果中出現(xiàn)一次)。
- 交集 - 交集沒(méi)有特殊的操作,因?yàn)槟阋呀?jīng)知道怎么去做。 像filter等鏈接函數(shù)在原始的QuerySet和新過(guò)濾器之前起了交集的作用。
- 差分 - 差分(數(shù)學(xué)上寫為qs1 \ qs2)代表所有在qs1而不在qs2中的項(xiàng)目。請(qǐng)注意,此操作是不對(duì)稱的(相對(duì)于以前的操作)。Python中恐怕沒(méi)有內(nèi)置的方式,但你可以這樣做:qs1.exclude(pk__in=qs2)
- 從空開始 - 開起來(lái)沒(méi)有用處但實(shí)際并非如此,正如上面例子所展示的。很多時(shí)候,當(dāng)我們動(dòng)態(tài)建立一個(gè)QuerySet聯(lián)合時(shí),我們需要從一個(gè)空列表開始,這是獲取它的方法:MyModel.objects.none().
- django queryset 去重 .distinct()說(shuō)明
- Python的Django框架實(shí)現(xiàn)數(shù)據(jù)庫(kù)查詢(不返回QuerySet的方法)
- Django ValuesQuerySet轉(zhuǎn)json方式
- Django框架 querySet功能解析
- django 中QuerySet特性功能詳解
- python實(shí)現(xiàn)合并多個(gè)list及合并多個(gè)django QuerySet的方法示例
- Python的Django框架中的select_related函數(shù)對(duì)QuerySet 查詢的優(yōu)化
- Django QuerySet查詢集原理及代碼實(shí)例
相關(guān)文章
python庫(kù)umap有效地揭示高維數(shù)據(jù)的結(jié)構(gòu)和模式初探
這篇文章主要介紹了python庫(kù)umap有效地揭示高維數(shù)據(jù)的結(jié)構(gòu)和模式初探,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Celery批量異步調(diào)用任務(wù)一直等待結(jié)果問(wèn)題
這篇文章主要介紹了Celery批量異步調(diào)用任務(wù)一直等待結(jié)果問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11教你用python將數(shù)據(jù)寫入Excel文件中
Python作為一種腳本語(yǔ)言相較于shell具有更強(qiáng)大的文件處理能力,下面這篇文章主要給大家介紹了關(guān)于如何用python將數(shù)據(jù)寫入Excel文件中的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02Python內(nèi)建函數(shù)之raw_input()與input()代碼解析
這篇文章主要介紹了Python內(nèi)建函數(shù)之raw_input()與input()代碼解析,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10phpsir 開發(fā) 一個(gè)檢測(cè)百度關(guān)鍵字網(wǎng)站排名的python 程序
一個(gè)檢測(cè)百度關(guān)鍵字網(wǎng)站排名的python 程序 phpsir 開發(fā)2009-09-09YOLOv5車牌識(shí)別實(shí)戰(zhàn)教程(六)性能優(yōu)化與部署
這篇文章主要介紹了YOLOv5車牌識(shí)別實(shí)戰(zhàn)教程(六)性能優(yōu)化與部署,在這個(gè)教程中,我們將一步步教你如何使用YOLOv5進(jìn)行車牌識(shí)別,幫助你快速掌握YOLOv5車牌識(shí)別技能,需要的朋友可以參考下2023-04-04