Django - queryset

Django ORM用到三個(gè)類:Manager丈秩、QuerySet、Model收毫。
Manager定義表級方法(表級方法就是影響一條或多條記錄的方法)奢人,我們可以以models.Manager為父類谓媒,定義自己的manager,增加表級方法何乎;QuerySet:Manager類的一些方法會(huì)返回QuerySet實(shí)例句惯,QuerySet是一個(gè)可遍歷結(jié)構(gòu),包含一個(gè)或多個(gè)元素宪赶,每個(gè)元素都是一個(gè)Model 實(shí)例宗弯,它里面的方法也是表級方法脯燃,前面說了搂妻,Django給我們提供了增加表級方法的途徑,那就是自定義manager類辕棚,而不是自定義QuerySet類欲主,一般的我們沒有自定義QuerySet類的必要邓厕;django.db.models模塊中的Model類,我們定義表的model時(shí)扁瓢,就是繼承它详恼,它的功能很強(qiáng)大,通過自定義model的instance可以獲取外鍵實(shí)體等引几,它的方法都是記錄級方法(都是實(shí)例方法昧互,貌似無類方法),不要在里面定義類方法伟桅,比如計(jì)算記錄的總數(shù)敞掘,查看所有記錄,這些應(yīng)該放在自定義的manager類中楣铁。以Django1.6為基礎(chǔ)玖雁。
每個(gè)Model都有一個(gè)默認(rèn)的manager實(shí)例,名為objects盖腕。

1.Qeuryset

每個(gè)Model都有一個(gè)默認(rèn)的manager實(shí)例赫冬,名為objects。QuerySet有兩種來源:通過manager的方法得到溃列、通過QuerySet的方法得到劲厌。
mananger的方法和QuerySet的方法大部分同名,同意思听隐,如filter(),update()等脊僚,但也有些不同,如manager有create()遵绰、get_or_create()辽幌,而QuerySet有delete()等,看源碼就可以很容易的清楚Manager類與Queryset類的關(guān)系椿访,Manager類的絕大部分方法是基于Queryset的乌企。一個(gè)QuerySet包含一個(gè)或多個(gè)model instance。QuerySet類似于Python中的list成玫,list的一些方法QuerySet也有加酵,比如切片,遍歷哭当。

>>> from userex.models import UserEx

>>> type(UserEx.objects)

<class ‘django.db.models.manager.Manager’>

>>> a = UserEx.objects.all()

>>> type(a)

<class ‘django.db.models.query.QuerySet’>

QuerySet是延遲獲取的猪腕,只有當(dāng)用到這個(gè)QuerySet時(shí),才會(huì)查詢數(shù)據(jù)庫求值钦勘。也就是懶加載陋葡!
另外,查詢到的QuerySet又是緩存的彻采,當(dāng)再次使用同一個(gè)QuerySet時(shí)腐缤,并不會(huì)再查詢數(shù)據(jù)庫捌归,而是直接從緩存獲取(不過岭粤,有一些特殊情況)惜索。一般而言,當(dāng)對一個(gè)沒有求值的QuerySet進(jìn)行的運(yùn)算剃浇,返回的是QuerySet巾兆、ValuesQuerySet、ValuesListQuerySet虎囚、Model實(shí)例時(shí)臼寄,一般不會(huì)立即查詢數(shù)據(jù)庫;反之溜宽,當(dāng)返回的不是這些類型時(shí)吉拳,會(huì)查詢數(shù)據(jù)庫。下面介紹幾種(并非全部)對QuerySet求值的場景适揉。

class Blog(models.Model):

    name = models.CharField(max_length=100)

    tagline = models.TextField()

 

    def __unicode__(self):

        return self.name

class Author(models.Model):

    name = models.CharField(max_length=50)

    email = models.EmailField()

 

    def __unicode__(self):

        return self.name

class Entry(models.Model):

    blog = models.ForeignKey(Blog)

    headline = models.CharField(max_length=255)

    body_text = models.TextField()

    pub_date = models.DateField()

    mod_date = models.DateField()

    authors = models.ManyToManyField(Author)

    n_comments = models.IntegerField()

    n_pingbacks = models.IntegerField()

    rating = models.IntegerField()

 

    def __unicode__(self):

        return self.headline

1)遍歷queryset

a = Entry.objects.all()

for e in a:

    print (e.headline)

當(dāng)遍歷一開始時(shí)留攒,先從數(shù)據(jù)庫執(zhí)行查詢select * from Entry得到a,然后再遍歷a嫉嘀。注意:這里只是查詢Entry表炼邀,返回的a的每條記錄只包含Entry表的字段值,不管Entry的model中是否有onetoone剪侮、onetomany拭宁、manytomany字段,都不會(huì)關(guān)聯(lián)查詢瓣俯。這遵循的是數(shù)據(jù)庫最少讀寫原則杰标。我們修改一下代碼,如下彩匕,遍歷一開始也是先執(zhí)行查詢得到a腔剂,但當(dāng)執(zhí)行print (e.blog.name)時(shí),還需要再次查詢數(shù)據(jù)庫獲取blog實(shí)體驼仪。

from django.db import connection

l = connection.queries  #l是一個(gè)列表掸犬,記錄SQL語句

a = Entry.objects.all()

for e in a:

    print (e.blog.name)

    len(l)

遍歷時(shí),每次都要查詢數(shù)據(jù)庫绪爸,l長度每次增1湾碎,Django提供了方法可以在查詢時(shí)返回關(guān)聯(lián)表實(shí)體,如果是onetoone或onetomany奠货,那用select_related介褥,不過對于onetomany,只能在主表(定義onetomany關(guān)系的那個(gè)表)的manager中使用select_related方法,即通過select_related獲取的關(guān)聯(lián)對象是model instance呻顽,而不能是QuerySet雹顺,如下丹墨,e.blog就是model instance廊遍。對于onetomany的反向和manytomany,要用prefetch_related贩挣,它返回的是多條關(guān)聯(lián)記錄喉前,是QuerySet。

a = Entry.objects.select_related('blog')

for e in a:

        print (e.blog.name)

        len(l)

可以看到從開始到結(jié)束王财,l的長度只增加1卵迂。另外,通過查詢connection.queries[-1]可以看到Sql語句用了join绒净。

2)queryset切片
切片不會(huì)立即執(zhí)行见咒,除非顯示指定了步長,如:

a= Entry.objects.all()[0:10:2]

步長為2挂疆。

3)序列化改览,即Pickling
序列化QuerySet很少用。

4)repr()
和str()功能相似缤言,將對象轉(zhuǎn)為字符串宝当,很少用。

5)len()
計(jì)算QuerySet元素的數(shù)量胆萧,并不推薦使用len()庆揩,除非QuerySet是求過值的(即evaluated),否則跌穗,用QuerySet.count()獲取元素?cái)?shù)量订晌,這個(gè)效率要高。

6)list()
將QuerySet轉(zhuǎn)為list蚌吸。list(qs)

7)bool()
判斷qs是否為空腾仅。

if Entry.objects.filter(headline="Test"):

     print("There is at least one Entry with the headline Test")

同樣不建議這種方法判斷是否為空,而應(yīng)該使用QuerySet.exists()套利,查詢效率高推励。


queryset的方法

數(shù)據(jù)庫的常用操作就四種:增、刪肉迫、改验辞、查,QuerySet的方法涉及刪喊衫、改跌造、查。后面還會(huì)講model對象的方法,model方法主要是增壳贪、刪陵珍、改、還有調(diào)用model實(shí)例的字段违施。

1)delete()
原型:delete()
返回:None

相當(dāng)于delete-from-where, delete-from-join-where互纯。先filter,然后對得到的QuerySet執(zhí)行delete()方法就行了磕蒲,它會(huì)同時(shí)刪除關(guān)聯(lián)它的那些記錄留潦,比如我刪除記錄表1中的A記錄,表2中的B記錄中有A的外鍵辣往,那同時(shí)也會(huì)刪除B記錄兔院,那ManyToMany關(guān)系呢?對于ManyToMany站削,刪除其中一方的記錄時(shí)坊萝,會(huì)同時(shí)刪除中間表的記錄,即刪除雙方的關(guān)聯(lián)關(guān)系许起。由于有些數(shù)據(jù)庫十偶,如Sqlite不支持delete與limit連用,所以在這些數(shù)據(jù)庫對QuerySet的切片執(zhí)行delete()會(huì)出錯(cuò)街氢。

>>> a = UserEx.objects.filter(is_active=False)

>>> b = a[:3]

>>> b.delete() #執(zhí)行時(shí)會(huì)報(bào)錯(cuò)

解決:UserEx.objects.filter(pk__in=b).delete() 

我們在用django開發(fā)項(xiàng)目的的時(shí)候扯键,經(jīng)常要和數(shù)據(jù)庫打交道,而django操作數(shù)據(jù)庫非常的方便珊肃,有很多非常簡便的方法讓你能夠快速的從數(shù)據(jù)庫里獲得你想要的數(shù)據(jù)荣刑。今天我就介紹給大家一個(gè)很好用的方法,那就是django in操作了我們經(jīng)常查數(shù)據(jù)庫的時(shí)候要把幾個(gè)符合條件的記錄都給查出來伦乔,那就要用到sql語句的in操作厉亏,那django怎么來執(zhí)行數(shù)據(jù)庫的in操作呢?接著看下面把烈和。有2個(gè)方法可以很好的實(shí)現(xiàn):1直接用filter語句里的方法來實(shí)現(xiàn)2用到extra方法比如我們要執(zhí)行:

select * from table where id in (3, 4, 5, 20)

用上面2個(gè)方法分別怎么操作呢?

django filter:Blog.objects.filter(pk__in=[3,4,5,20])
django extra:Blog.objects.extra(where=['id IN (3, 4, 5, 20)'])

2) update()

批量修改爱只,返回修改的記錄數(shù)。不過update()中的鍵值對的鍵只能是主表中的字段招刹,不能是關(guān)聯(lián)表字段恬试,如下:

Entry.objects.update(blog__name='foo')  #錯(cuò)誤,無法修改關(guān)聯(lián)表字段疯暑,只能修改Entry表的字段

Entry.objects.filter(blog__name='foo').update(comments_on=False)  #正確

最好的方法是先filter训柴,查詢出QuerySet,然后再執(zhí)行QuerySet.update()妇拯。

由于有些數(shù)據(jù)庫幻馁,不支持update與limit連用洗鸵,所以在這些數(shù)據(jù)庫對QuerySet的切片執(zhí)行update()會(huì)出錯(cuò)。

3)filter(kwargs)仗嗦、exclude(kwargs)膘滨、get(**kwargs)

相當(dāng)于select-from-where,select-from-join-where稀拐,很多網(wǎng)站讀數(shù)據(jù)庫操作最多火邓。可以看到钩蚊,filter()的參數(shù)是變個(gè)數(shù)的鍵值對贡翘,而不會(huì)出現(xiàn)>,<,!=等符號蹈矮,這些符號分別用__gt,__lt,~Q或exclude()砰逻,不過對于!=,建議使用Q查詢泛鸟,更不容易出錯(cuò)蝠咆。可以使用雙下劃線對OneToOne北滥、OneToMany刚操、ManyToMany進(jìn)行關(guān)聯(lián)查詢和反向關(guān)聯(lián)查詢,而且方法都是一樣的再芋,如:

>>> Entry.objects.filter(blog__name='Beatles Blog') #限定外鍵表的字段

#下面是反向連接菊霜,不過要注意,這里不是entry_set济赎,entry_set是Blog instance的一個(gè)屬性鉴逞,代表某個(gè)Blog object

#的關(guān)聯(lián)的所有entry,而QuerySet的方法中反向連接是直接用model的小寫司训,不要把兩者搞混构捡。It works backwards,

#too. To refer to a “reverse” relationship, just use the lowercase name of the model.

>>> Blog.objects.filter(entry__headline__contains='Lennon')

>>> Blog.objects.filter(entry__authors__name='Lennon')   #ManyToMany關(guān)系,反向連接

>>> myblog = Blog.objects.get(id=1)

>>> Entry.objects.filter(blog=myblog)  #正向連接壳猜。與下面一句等價(jià)勾徽,既可以用實(shí)體,也可以用

#實(shí)體的主鍵统扳,其實(shí)即使用實(shí)體喘帚,也是只用實(shí)體的主鍵而已。這兩種方式對OneToOne咒钟、

#OneToMany吹由、ManyToMany的正向、反向連接都適用盯腌。

>>> Entry.objects.filter(blog=1)    #我個(gè)人不建議這樣用溉知,對于create(),不支持這種用法

>>> myentry = Entry.objects.get(id=1)

>>> Blog.objects.filter(entry=myentry) #ManyToMany反向連接。與下面兩種方法等價(jià)

>>> Blog.objects.filter(entry=1)   

>>> Blog.objects.filter(entry_id=1)  #適用于OneToOne和OneToMany的正向連接

OneToOne的關(guān)系也是這樣關(guān)聯(lián)查詢级乍,可以看到舌劳,Django對OneToOne、OneToMany玫荣、ManyToMany關(guān)聯(lián)查詢及其反向關(guān)聯(lián)查詢提供了相同的方式甚淡,真是牛逼啊。對于OneToOne捅厂、OneToMany的主表贯卦,也可以使用下面的方式

Entry.objects.filter(blog_id=1),因?yàn)閎log_id是數(shù)據(jù)庫表Entry的一個(gè)字段焙贷, 這條語句與Entry.objects.filter(blog=blog1)生成的SQL是完全相同的撵割。

與filter類似的還有exclude(**kwargs)方法,這個(gè)方法是剔除辙芍,相當(dāng)于select-from-where not啡彬。可以使用雙下劃線對OneToOne故硅、OneToMany庶灿、ManyToMany進(jìn)行關(guān)聯(lián)查詢和反向關(guān)聯(lián)查詢,方法與filter()中的使用方法相同吃衅。

>>> Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

轉(zhuǎn)為SQL為

SELECT *

FROM Entry

WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

4)SQL其它關(guān)鍵字在django中的實(shí)現(xiàn)

在SQL中往踢,很多關(guān)鍵詞在刪、改徘层、查時(shí)都是可以用的峻呕,如order by、 like惑灵、in山上、join、union英支、and佩憾、or、not等等干花,我們以查詢?yōu)槔保f一下django如何映射SQL的這些關(guān)鍵字的(查、刪池凄、改中這些關(guān)鍵字的使用方法基本相同)抡驼。

a. F類(無對應(yīng)SQL關(guān)鍵字)

前面提到的filter/exclude中的查詢參數(shù)值都是常量,如果我們想比較model的兩個(gè)字段怎么辦呢肿仑?Django也提供了方法致盟,F(xiàn)類碎税,F(xiàn)類實(shí)例化時(shí),參數(shù)也可以用雙下劃線馏锡,也可以邏輯運(yùn)算雷蹂,如下:

>>> from django.db.models import F

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

>>> from datetime import timedelta

>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

>>> Entry.objects.filter(authors__name=F('blog__name'))

b.Q類(對應(yīng)and/or/not)

如果有or等邏輯關(guān)系呢,那就用Q類杯道,filter中的條件可以是Q對象與非Q查詢混和使用匪煌,但不建議這樣做,因?yàn)榛旌筒樵儠r(shí)Q對象要放前面党巾,這樣就有難免忘記順序而出錯(cuò)萎庭,所以如果使用Q對象,那就全部用Q對象。Q對象也很簡單,就是把原來filter中的各個(gè)條件分別放在一個(gè)Q()即可创葡,不過我們還可以使用或與非,分別對應(yīng)符號為”|”和”&”和”~”达舒,而且這些邏輯操作返回的還是一個(gè)Q對象值朋,另外叹侄,逗號是各組條件的基本連接符,也是與的關(guān)系昨登,其實(shí)可以用&代替(在python manage.py shell測試過趾代,&代替逗號,執(zhí)行的SQL是一樣的)丰辣,不過那樣的話可讀性會(huì)很差撒强,這與我們直接寫SQL時(shí),各組條件and時(shí)用換行一樣笙什,邏輯清晰:

from django.db.models import Q

>>> Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),

question__startswith='Who')   #正確飘哨,但不要這樣混用

>>> Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),

Q(question__startswith='Who'))  #推薦,全部是Q對象

>>> Poll.objects.get( (Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))&

Q(question__startswith='Who'))  #與上面語句同意琐凭,&代替”,”芽隆,可讀性差

Q類中時(shí)應(yīng)該可以用F類,待測試统屈。

c.annotate(無對應(yīng)SQL關(guān)鍵字)

函數(shù)原型annotate(*args, **kwargs)

返回QuerySet

往每個(gè)QuerySet的model instance中加入一個(gè)或多個(gè)字段胚吁,字段值只能是聚合函數(shù),因?yàn)槭褂胊nnotate時(shí)愁憔,會(huì)用group by腕扶,所以只能用聚合函數(shù)。聚合函數(shù)可以像filter那樣關(guān)聯(lián)表吨掌,即在聚合函數(shù)中半抱,Django對OneToOne脓恕、OneToMany、ManyToMany關(guān)聯(lián)查詢及其反向關(guān)聯(lián)提供了相同的方式窿侈,見下面例子进肯。

>>> from django.contrib.auth.models import User

>>> from django.db.models import Count

#計(jì)算每個(gè)用戶的userjob數(shù)量,字段命名為ut_num棉磨,返回的QuerySet中的每個(gè)object都有

#這個(gè)字段江掩。在UserJob中定義User為外鍵,在Job中定義與User是ManyToMany

>>> a = User.objects.filter(is_active=True, userjob__is_active=True). annotate(n=Count(‘userjob’)) #一對多反向連接

>>> b = User.objects.filter(is_active=True, job__is_active=True).annotate(n=Count(‘job__name’))  #多對多反向連接乘瓤,User與Job是多對多

>>> len(a)  #這里才會(huì)對a求值

>>> len(b)  #這里才會(huì)對b求值

a對應(yīng)的SQL語句為(SQL中沒有為表起別名,u环形、ut是我加的):

select auth.user.*,Count(ut.id) as ut_num

from auth_user as u

left outer join job_userjob as ut on u.id = ut.user_id

where u.is_active=True and ut.is_active=True

group by u.*

b對應(yīng)的SQL語句為(SQL中沒有為表起別名,u、t衙傀、r是我加的):

select u.*,Count(t.name) as n

from auth_user as u

left outer join job_job_users as r on u.id=r.user_id

left outer join job_job as t on r.job_id=t.id

where t.is_active=True and u.is_active=True

group by u.*

d. order_by——對應(yīng)order by

函數(shù)原型 order_by(*fields)

返回QuerySet

正向的反向關(guān)聯(lián)表跟filter的方式一樣抬吟。如果直接用字段名,那就是升序asc排列统抬;如果字段名前加-火本,就是降序desc

e.distinct——對應(yīng)distinct
原型 distinct()

一般與values()、values_list()連用聪建,這時(shí)它返回ValuesQuerySet钙畔、ValuesListQuerySet

這個(gè)類跟列表很相似,它的每個(gè)元素是一個(gè)字典金麸。它沒有參數(shù)(其實(shí)是有參數(shù)的擎析,不過,參數(shù)只在PostgreSQL上起作用)挥下。使用方法為:

>>> a=Author.objects.values_list(name).distinct()

>>> b=Author.objects.values_list(name,email).distinct()

對應(yīng)的SQL分別為:

select distinct name

from Author

select distinct name,email

from Author

distinct的了解:http://www.cnblogs.com/rainman/archive/2013/05/03/3058451.html

f.values()和values_list()——對應(yīng)‘select 某幾個(gè)字段’

可以參考的連接:http://blog.csdn.net/tmpbook/article/details/50297403

函數(shù)原型values(field), values_list(field)
返回ValuesQuerySet, ValuesListQuerySet

Author.objects.filter(*kwargs)對應(yīng)的SQL只返回主表(即Author表)的所有字段值揍魂,即使在查詢時(shí)關(guān)聯(lián)了其它表,關(guān)聯(lián)表的字段也不會(huì)返回棚瘟,只有當(dāng)我們通過Author instance用關(guān)聯(lián)表時(shí)现斋,Django才會(huì)再次查詢數(shù)據(jù)庫獲取值。當(dāng)我們不用Author instance的方法偎蘸,且只想返回幾個(gè)字段時(shí)庄蹋,就要用values(),它返回的是一個(gè)ValuesQuerySet對象禀苦,它類似于一個(gè)列表蔓肯,不過,它的每個(gè)元素是字典振乏。而values_list()跟values()相似蔗包,它返回的是一個(gè)ValuesListQuerySet,也類型于一個(gè)列表慧邮,不過它的元素不是字典调限,而是元組舟陆。一般的,當(dāng)我們不需要model instance的方法且返回多個(gè)字段時(shí)耻矮,用values(field)秦躯,而返回單個(gè)字段時(shí)用values_list(‘field’,flat=True),這里flat=True是要求每個(gè)元素不是元組裆装,而是單個(gè)值踱承,見下面例子。而且我們可以返回關(guān)聯(lián)表的字段哨免,用法跟filter中關(guān)聯(lián)表的方式完全相同茎活。

>>> a = User.objects.values(‘id’,’username’,’userex__age’)

>>> type(a)

<class ‘django.db.models.query.ValuesQuerySet’>

>>> a

[{‘id’:0,’username’:u’test0’,’ userex__age’: 20},{‘id’:1,’username’:u’test1’,’userex__age’: 25},

 {‘id’:2,’username’:u’test2’, ’ userex__age’: 28}]

>>> b= User.objects.values_list(’username’,flat=True)

>>> b

[u’test0’, u’test1’ ,u’test2’]

g.select_related()——對應(yīng)返回關(guān)聯(lián)記錄實(shí)體

原型select_related(*filed)

返回QuerySet

它可以指定返回哪些關(guān)聯(lián)表model instance,這里的field跟filter()中的鍵一樣琢唾,可以用雙下劃線载荔,但也有不同,You can refer to any ForeignKey or OneToOneField relation in the list of fields passed to select_related()采桃,QuerySet中的元素中的OneToOne關(guān)聯(lián)及外鍵對應(yīng)的是都是關(guān)聯(lián)表的一條記錄懒熙,如my_entry=Entry.objects.get(id=1),my_entry.blog就是關(guān)聯(lián)表的一條記錄的對象普办。select_related()不能用于OneToMany的反向連接工扎,和ManyToMany,這些都是model的一條記錄對應(yīng)關(guān)聯(lián)表中的多條記錄泌豆。前面提到了對于a = Author.objects.filter(**kwargs)這類語句定庵,對應(yīng)的SQL只返回主表,即Author的所有字段踪危,并不會(huì)返回關(guān)聯(lián)表字段值,只有當(dāng)我們使用關(guān)聯(lián)表時(shí)才會(huì)再查數(shù)據(jù)庫返回猪落,但有些時(shí)候這樣做并不好贞远。看下面兩段代碼笨忌,這兩段代碼在1.1中提到過蓝仲。在代碼1中,在遍歷a前官疲,先執(zhí)行a對應(yīng)的SQL袱结,拿到數(shù)據(jù)后,然后再遍歷a途凫,而遍歷過程中垢夹,每次都還要查詢數(shù)據(jù)庫獲取關(guān)聯(lián)表。代碼2中维费,當(dāng)遍歷開始前果元,先拿到Entry的QuerySet促王,并且也拿到這個(gè)QuerySet的每個(gè)object中的blog對象,這樣遍歷過程中而晒,就不用再查詢數(shù)據(jù)庫了蝇狼,這樣就減少了數(shù)據(jù)庫讀次數(shù)。

代碼1

a = Entry.objects.all()

for e in a:

    print (e.blog.name)

 

代碼2

a = Entry.objects.select_related('blog')

for e in a:

        print (e.blog.name)

h. prefetch_related(*field) ——對應(yīng)返回關(guān)聯(lián)記錄實(shí)體的集合

函數(shù)原型prefetch_related(*field)
返回的是QuerySet

這里的field跟filter()中的鍵一樣倡怎,可以用雙下劃線迅耘。用于OneToMany的反向連接,及ManyToMany监署。其實(shí)豹障,prefetch_related()也能做select_related()的事情,但由于策略不同焦匈,可能相比select_related()要低效一些血公,所以建議還是各管各擅長的。select_related是用select ……join來返回關(guān)聯(lián)的表字段缓熟,而prefetch_related是用多條SQL語句的形式查詢累魔,一般,后一條語句用IN來調(diào)用上一句話返回的結(jié)果够滑。

class Restaurant(models.Model):

    pizzas = models.ManyToMany(Pizza, related_name='restaurants')

    best_pizza = models.ForeignKey(Pizza, related_name='championed_by')

 

>>> Restaurant.objects.prefetch_related('pizzas__toppings')

>>> Restaurant.objects.select_related('best_pizza').prefetch_related('best_pizza__toppings')

先用select_related查到best_pizza對象垦写,再用prefetch_related 從best_pizza查出toppings

i.extra()——實(shí)現(xiàn)復(fù)雜的where子句

函數(shù)原型:extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

基本上,查詢時(shí)用django提供的方法就夠用了彰触,不過有時(shí)where子句中包含復(fù)雜的邏輯梯投,這種情況下django提供的方法可能不容易做到,還好况毅,django有extra()分蓖, extra()中直接寫一些SQL語句。不過尔许,不同的數(shù)據(jù)庫用的SQL有些差異么鹤,所以盡可能不要用extra()。需要時(shí)再看使用方法吧味廊。

j. aggregate(*args, **kwargs)——對應(yīng)聚合函數(shù)

參數(shù)為聚合函數(shù)蒸甜,最好用**kwargs的形式,每個(gè)參數(shù)起一個(gè)名字余佛。

該函數(shù)與annotate()有何區(qū)別呢柠新?annotate相當(dāng)于aggregate()和group by的結(jié)合,對每個(gè)group執(zhí)行aggregate()函數(shù)辉巡。而單獨(dú)的aggregate()并沒有g(shù)roup by恨憎。

>>> from django.db.models import Count

>>> q = Blog.objects.aggregate(Count('entry'))  #這是用*args的形式,最好不要這樣用

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))  #這是用**kwargs的形式

{'number_of_entries': 16}
至此红氯,我們總結(jié)了QuerySet方法返回的數(shù)據(jù)形式框咙,主要有五種咕痛。第一種:返回QuerySet,每個(gè)object只包含主表字段喇嘱;第二種:返回QuerySet茉贡,每個(gè)object除了包含主表所有字段,還包含某些關(guān)聯(lián)表的object者铜,這種情況要用select_related()和prefetch_related()腔丧,可以是任意深度(即任意多個(gè)雙下劃線)的關(guān)聯(lián),通常一層關(guān)聯(lián)和二層關(guān)聯(lián)用的比較多作烟;第三種:返回ValuesQuerySet, ValuesListQuerySet愉粤,它們的每個(gè)元素包含若干主表和關(guān)聯(lián)表的字段,不包含任何實(shí)體和關(guān)聯(lián)實(shí)例拿撩,這種情況要用values()和values_list()衣厘;第四種:返回model instance;第五種:單個(gè)值压恒,如aggregate()方法影暴。

k.exists()、count()探赫、len()

如果只是想知道一個(gè)QuerySet是否為空型宙,而不想獲取QuerySet中的每個(gè)元素,那就用exists()伦吠,它要比len()妆兑、count()、和直接進(jìn)行if判斷效率高毛仪。如果只想知道一個(gè)QuerySet有多大搁嗓,而不想獲取QuerySet中的每個(gè)元素,那就用count()潭千;如果已經(jīng)從數(shù)據(jù)庫獲取到了QuerySet谱姓,那就用len()。
上面也有提到刨晴。

l. contains/startswith/endswith——對應(yīng)like

字段名加雙下劃線,除了它路翻,還有icontains狈癞,即Case-insensitive contains,這個(gè)是大小寫不敏感的茂契,這需要相應(yīng)數(shù)據(jù)庫的支持蝶桶。有些數(shù)據(jù)庫需要設(shè)置

才能支持大小寫敏感。

m.in——對應(yīng)in

字段名加雙下劃線掉冶。

n.exclude(field__in=iterable)——對應(yīng)not in
iterable是可迭代對象

o.gt/gte/lt/lte——對應(yīng)于>,>=,<,<=
字段名加雙下劃線

p.range——對應(yīng)于between and

字段名加雙下劃線真竖,range后面值是列表

q. isnull——對應(yīng)于is null

Entry.objects.filter(pub_date__isnull=True)對應(yīng)的SQL為SELECT ... WHERE pub_date IS NULL;

r.QuerySet切片——對應(yīng)于limit

QuerySet的索引只能是非負(fù)整數(shù)脐雪,不支持負(fù)整數(shù),所以QuerySet[-1]錯(cuò)誤

a=Entry.objects.all()[5:10]
b=len(a) 

執(zhí)行Entry.objects.all()[5:8]恢共,對于不同的數(shù)據(jù)庫战秋,SQL語句不同,

Sqlite 的SQL語句為
select * from tablename limit 3 offset 5; 
MySQL的SQL語句為
select * from tablename limit 5,3;

經(jīng)驗(yàn)
queryset 的關(guān)聯(lián)查詢:

qs = Disk.objects.filter(hostType=obj).values(
            "id","diskEssenceType__name", "diskOsType__name", "hostType__name"
        )
class HostTypeSerializer(ModelSerializer):
    cputypes = CPUTypeSerializer(many=True, read_only=True)
    memtypes = MEMTypeSerializer(many=True, read_only=True)
    bandwidths = BandWidthTypeSerializer(many=True, read_only=True)
    #disks = DiskSerializer(many=True, read_only=True)
    disks = serializers.SerializerMethodField()
    class Meta:
        model = HostType
        fields = [
            "id",
            "name",
            "cputypes",
            "memtypes",
            "bandwidths",
            "disks",
        ]
    def get_disks(self, obj):

        qs = Disk.objects.filter(hostType=obj).values(
            "id","diskEssenceType__name", "diskOsType__name", "hostType__name"
        )

        dict = {"系統(tǒng)盤": [], "數(shù)據(jù)盤": []}

        for item in qs:
            if item['diskOsType__name'] == "系統(tǒng)盤":
                dict["系統(tǒng)盤"].append(item)
            elif item['diskOsType__name'] == "數(shù)據(jù)盤":
                dict["數(shù)據(jù)盤"].append(item)

        disk_list = [{"id":1, "type":"系統(tǒng)盤", "details":[]}, {"id":2, "type":"數(shù)據(jù)盤", "details":[]}]

        for os_disk in dict["系統(tǒng)盤"]:
            disk_list[0]["details"].append(os_disk)
        for data_dist in dict["數(shù)據(jù)盤"]:
            disk_list[1]["details"].append(data_dist)

        return disk_list

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末讨韭,一起剝皮案震驚了整個(gè)濱河市脂信,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌透硝,老刑警劉巖狰闪,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異濒生,居然都是意外死亡埋泵,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門罪治,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丽声,“玉大人,你說我怎么就攤上這事规阀『阈颍” “怎么了?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵谁撼,是天一觀的道長歧胁。 經(jīng)常有香客問我,道長厉碟,這世上最難降的妖魔是什么喊巍? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮箍鼓,結(jié)果婚禮上崭参,老公的妹妹穿的比我還像新娘。我一直安慰自己款咖,他們只是感情好何暮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铐殃,像睡著了一般海洼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上富腊,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天坏逢,我揣著相機(jī)與錄音,去河邊找鬼。 笑死是整,一個(gè)胖子當(dāng)著我的面吹牛肖揣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浮入,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼龙优,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舵盈?” 一聲冷哼從身側(cè)響起陋率,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秽晚,沒想到半個(gè)月后瓦糟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赴蝇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年菩浙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片句伶。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劲蜻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出考余,到底是詐尸還是另有隱情先嬉,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布楚堤,位于F島的核電站疫蔓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏身冬。R本人自食惡果不足惜衅胀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酥筝。 院中可真熱鬧滚躯,春花似錦、人聲如沸嘿歌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宙帝。三九已至阅束,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茄唐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沪编,地道東北人呼盆。 一個(gè)月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像蚁廓,于是被迫代替她去往敵國和親访圃。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354

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