django(8)QuerySet API

模型.objects:

這個對象是django.db.models.manager.Manager的對象,這個類是一個空殼類,他上面的所有方法都是從QuerySet這個類上面拷貝過來的。因此我們只要學(xué)會了QuerySet,這個objects也就知道該如何使用了。
Manager源碼解析:

class_name = "BaseManagerFromQuerySet"

class_dict = {
    '_queryset_class': QuerySet
}

class_dict.update(cls._get_queryset_methods(QuerySet))

# type動態(tài)的時候創(chuàng)建類
# 第一個參數(shù)是用來指定創(chuàng)建的類的名字。創(chuàng)建的類名是:BaseManagerFromQuerySet
# 第二個參數(shù)是用來指定這個類的父類。
# 第三個參數(shù)是用來指定這個類的一些屬性和方法
return type(class_name,(cls,),class_dict)

_get_queryset_methods:這個方法就是將QuerySet中的一些方法拷貝出來

filter/exclude/annotate:過濾/排除滿足條件的/給模型添加新的字段。

order_by:

# 根據(jù)創(chuàng)建的時間正序排序
articles = Article.objects.order_by("create_time")
# 根據(jù)創(chuàng)建的時間倒序排序
articles = Article.objects.order_by("-create_time")
# 根據(jù)作者的名字進(jìn)行排序
articles = Article.objects.order_by("author__name")
# 首先根據(jù)創(chuàng)建的時間進(jìn)行排序,如果時間相同,則根據(jù)作者的名字進(jìn)行排序
articles = Article.objects.order_by("create_time",'author__name')

一定要注意的一點是,多個order_by,會把前面排序的規(guī)則給打亂,而使用后面的排序方式。比如以下代碼:

articles = Article.objects.order_by("create_time").order_by("author__name")

他會根據(jù)作者的名字進(jìn)行排序,而不是使用文章的創(chuàng)建時間。
當(dāng)然,也可以在模型定義的在Meta類中定義ordering來指定默認(rèn)的排序方式。示例代碼如下:

    class Meta:
        db_table = 'book_order'
        ordering = ['create_time','-price']

還可以根據(jù)annotate定義的字段進(jìn)行排序。比如要實現(xiàn)圖書的銷量進(jìn)行排序,那么示例代碼如下:

books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums")
    for book in books:
        print('%s/%s'%(book.name,book.order_nums))

values:

有時候我們在表中查找數(shù)據(jù)的時候,并不是想把所有的字段都提取出來。我們有可能只想要其中的幾個字段,這時候就可以使用values來實現(xiàn),需要幾個字段,就把這幾個字段的名字傳遞到這個方法中,示例代碼如下:

books = Book.objects.values('id', 'name')

values的返回值同樣是一個QuerySet對象,但其中不在是模型,而是字典。

如果我們想要提取的是這個模型上關(guān)聯(lián)的對象屬性,那么也是可以的,查找順序跟filter的用法是一樣的,示例代碼如下:

books = Book.objects.values('id', 'name', 'author__name')

以上將會提取authorname字段,如果我們不想使用這個名字。那么可以使用關(guān)鍵字 參數(shù),示例代碼如下:

books = Book.objects.values("id", "name", "author_name=F('author__name')")

注意:自定義的名字,不能和模型本身擁有的字段一樣,比如以上author_name取名author就會報錯,因為Book上本身就有author字段名字。

values中,也可以使用聚合函數(shù)來形成一個新的字段,比如我想要獲取每本圖書的銷量,那么示例代碼如下:

books = Book.objects.values('id', 'name', order_nums=Count('bookorder'))

如果調(diào)用values方法時,沒有傳遞任何參數(shù),那么會獲取這個模型上的所有的字段以及對應(yīng)的值形成的字典,示例代碼如下:

books = Book.objects.values()

那么books中的值如下:

{'id': 1, 'name': '三國演義', 'pages': 987, 'price': 108.0, 'rating': 3.9, 'author_id': 3, 'publisher_id': 1}

values_list

values是一樣的作用,只不過這個方法返回的QuerySet中,裝的不是字典,而是元組,示例代碼如下:

 books = Book.objects.values_list('id', 'name')

那么以上代碼返回的結(jié)果時:

(1, '三國i演義')

如果給values_list只指定一個字段,那我們可以指定flat=True,這樣返回回來的結(jié)果就不在是一個元組,而是這個字段的值,示例代碼如下:

books = Book.objects.values_list('name', flat=True)

那么以上返回的結(jié)果是:

'三國演義'

一點要注意的是,flat只能用在一個字段的情況下,否則就會報錯。

all:

這個方法簡單的返回一個QuerySet對象,這個QuerySet對象沒有經(jīng)過任何的修改(比如:過濾等)

select_related:

在查找某個表的數(shù)據(jù)的時候,可以一次性把相關(guān)聯(lián)的其他表的數(shù)據(jù)都提取出來,這樣可以在以后訪問相關(guān)聯(lián)的表的數(shù)據(jù)的時候,不用再次查詢數(shù)據(jù)庫,可以節(jié)省一些開銷。示例代碼如下:

books = Book.objects.select_related("author", ''publisher")
for book in books:
    print(book.author.name)
    # 因為在提取book時后,使用了select_related,
    # 那么以后再訪問book.author的時候,不會再次項數(shù)據(jù)庫重新發(fā)起查詢

注意:這個方法只能用在外鍵關(guān)聯(lián)的對象上,對于那種多對多、或者多對一的情況,不能使用它來實現(xiàn),而應(yīng)該使用prefetch_related來實現(xiàn)。

prefetch_related:

這個方法類似于select_related方法,也是用來在查詢語句的時候,提前將指定的數(shù)據(jù)找出來,只不過這個方法用來解決多對多,或者多對一的情況,這個方法產(chǎn)生兩個查詢語句。所以,若果在這個方法中查詢使用外鍵關(guān)聯(lián)的模型時,也會產(chǎn)生兩個查詢語句,因此如果查詢的是外鍵關(guān)聯(lián)的模型建議使用select_related方法。在查詢多對多或者多對一的關(guān)聯(lián)對象的時候,在使用模型怎么訪問這個多對多,那么就在這個方法中傳遞什么字符串,比如要獲取圖書上的所有訂單,那么示例代碼如下:

books = Book.objects.preftch_related(''bookorder_set")

需要注意的是:在使用preftch_related查找出來的bookorder_ser,建議不要再對他進(jìn)行任何操作,不如filter,不然又會產(chǎn)生N多查詢語句,比如以下代碼是不對的:

books = Book.objects.preftch_related(''bookorder_set")
for book in books:
    print('='*30)
    print(book.name)
    orders = book.bookorder_set.filter(price__gte=90)
    for order in orders:
        print(order.id)

那么如果確實像要對預(yù)先查找的集合進(jìn)行操作,那么我們可以使用django.db.models.Prefetch來完成,示例代碼如下:

form django.db.models import Prefetch
# 先使用Prefetch把查找的條件寫好,再放到prefetch_related中
prefetch = Prefetch("bookorder_set", queryset=BookOrder.objects.filter(price__gte=90))
books = Book.objects.preftch_related(prefetch)
for book in books:
    print('='*30)
    print(book.name)
    orders = book.bookorder_set.all()
    for order in orders:
        print(order.id)

defer和only:

這個方法都會返回一個QuerySet對象,并且這個QuerySet中裝的都是模型,而不是字典。
1、defer: 這個方法用來告訴ORM, 在查詢某個模型的時候,過濾掉某些字段。
2、only: 這個方法用來告訴ORM,在查詢某個模型的時候,只提取某幾個字段。
注意:使用defer或者only,如果沒有提取這個字段,那么后面使用這個字段,會重新發(fā)起一次請求,因此要謹(jǐn)慎使用。

get:

獲取滿足條件的值,這個方法給定的條件只能匹配到一條數(shù)據(jù),如果匹配到多條數(shù)據(jù)或者沒有數(shù)據(jù),它都會報錯,因此使用時,都是根據(jù)主鍵為條件來匹配的。

create:

可以創(chuàng)建一條數(shù)據(jù),并且將這條數(shù)據(jù)保存到數(shù)據(jù)庫中,以下代碼是等價的。

publisher = Publisher(name='xxxx出版社')
publisher.save()
# 等價于下面這句
Publisher.objects.create(name='xxxx出版社')

get_or_create:

如果給定的條件有數(shù)據(jù),那么就會把這個數(shù)據(jù)給提取出來,如果給定的條件沒有數(shù)據(jù),就會先創(chuàng)建數(shù)據(jù),然后把數(shù)據(jù)給返回回來。

bulk_creat:

一次性創(chuàng)建多個數(shù)據(jù),示例代碼如下:

publisher = Publisher.objects.bulk_create([
    Publisher(name='123出版社'),
    Publisher(name='456出版社')
])

count:

獲取提取的數(shù)據(jù)個數(shù),如果想要知道總共有多少條數(shù)據(jù),那么建議使用count,而不是使用len(books)這種,因為Count在底層是使用select count(*)來實現(xiàn)的,這種方式比使用len函數(shù)更加高效。

first 和 last

返回QuerySet中第一和最后一條數(shù)據(jù)

exists:

判斷某個條件的數(shù)據(jù)是否存在,如果判斷某個條件的元素是否存在,建議使用exists,這個比使用count或者直接判斷QuerySet更有效的多。示例代碼如下:

if Article.objects.filter(title__contains='hello').exists():
    print(True)
# 比使用count更高效
if Article.objects.filter(title__contains='hello').count() > 0:
    print(True)
# 也比直接判斷QuerySet更高效
if Article.objects.filter(title__contains='hello'):
    print(True)

distinct:

去掉那些重復(fù)的數(shù)據(jù),這個方法如果底層數(shù)據(jù)庫使用的是MySQL,那么不能傳遞任何參數(shù),比如想要提取所有銷售價格超過80元的圖書,并且刪除掉那些重復(fù)的,那么可以使用distinct來幫我們實現(xiàn),示例代碼如下:

books = Book.objects.filter(bookorder__price__gte=80).distinct()

需要注意的是,如果distinct之前使用了order_by,那么order_by會提取order_by中指定的字段,因此,再使用distinct1就會根據(jù)多個字段來進(jìn)行唯一化,就不會把那些重復(fù)的數(shù)據(jù)刪除掉,示例代碼如下:

orders = BookOrder.objects.order_by('create_time').values('book_id').distinct()
# 那么以上代碼因為使用了order_by,即使使用了distinct,也會把重復(fù)的book_id提取出來

update和delete:

一次性可以把所有的數(shù)據(jù)都更新,
一次性可以把滿足條件的數(shù)據(jù)都刪除掉,但需要注意的刪除數(shù)據(jù)的時候,要注意on_delete指定的處理方式。

切片操作:

有時候我們查找數(shù)據(jù),有可能只需要其中的一部分,那么這時候可以使用切片操作來幫助我們完成。QuerySet使用切片操作就跟列表使用切片操作一樣,示例代碼如下:

books = Book.objects.all()[1:3]
for book in books:
    print(book)

切片操作并不是把所有的數(shù)據(jù)從數(shù)據(jù)庫中提取出來再做切片操作,而是在數(shù)據(jù)庫層面使用LIMIT和OFFSET來幫助我們完成。

什么時候 Django 會將 QuerySet 轉(zhuǎn)換為 SQL 去執(zhí)行:

1、迭代
2、使用步長做切片操作
3、調(diào)用len函數(shù)
4、調(diào)用list函數(shù):將一個QuerySet對象轉(zhuǎn)換為list對象也會立即執(zhí)行SQL語句。
5、判斷:如果對某個QuerySet進(jìn)行片段,也會立馬執(zhí)行 SQL 語句。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽光小鎮(zhèn)少爺閱讀 3,963評論 0 8
  • Django 準(zhǔn)備 “虛擬環(huán)境為什么需要虛擬環(huán)境:到目前位置,我們所有的第三方包安裝都是直接通過 pip inst...
    33jubi閱讀 1,393評論 0 5
  • 曾經(jīng)發(fā)生一些事,心里有一些感受,當(dāng)時處理不了,大腦會自己動壓抑到邊緣地帶!但是它一直都在,它一直在尋求表達(dá),想被看...
    范范A閱讀 1,083評論 1 1
  • 一個情商低的男人表現(xiàn)可謂千變?nèi)f化,而情商低的男友又是女孩子們經(jīng)常吐槽的對象,那我就說說在戀愛中的幾種表現(xiàn)吧: 初級...
    過期白糖閱讀 1,074評論 1 5
  • 1、家里養(yǎng)了一只禿毛狗,就是為了和我的禿頂遙相呼應(yīng)。 2、狗是人類的好朋友,豬表示不服。 3、現(xiàn)在城市里很多人都喜...
    1e81dcf20286閱讀 279評論 0 0

友情鏈接更多精彩內(nèi)容