国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Django之QuerySet對象與查詢優(yōu)化

這篇具有很好參考價值的文章主要介紹了Django之QuerySet對象與查詢優(yōu)化。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

一、QuerySet對象

Django的ORM中存在查詢集的概念。

查詢集,也稱查詢結(jié)果集,即QuerySet,表示從數(shù)據(jù)庫中獲取的對象集合。

當(dāng)調(diào)用如下過濾器方法時,Django會返回查詢集(與列表類似,但不是簡單的列表):

all():返回所有數(shù)據(jù)。

filter():返回滿足條件的數(shù)據(jù)。

exclude():返回滿足條件之外的數(shù)據(jù)。

order_by():對結(jié)果進(jìn)行排序。

1、可切片

可以使用Python 的切片語法來限制查詢集記錄的數(shù)目,它等同于SQL 的LIMIT 和OFFSET 子句 。

res = Book.objects.all()[:5]  # limit 5
print(res)  # <QuerySet [<Book: book1>, ..., <Book: book5>]>

res = Book.objects.all()[5:7]   # LIMIT 2 OFFSET 5,偏移5所以從6開始,到7結(jié)束即limit 2
print(res)  # <QuerySet [<Book: book6>, <Book: book7>]>

不支持負(fù)的索引(例如Book.objects.all()[-1])。

強(qiáng)調(diào):不能把QuerySet單純地當(dāng)成python中的列表,它們還是有區(qū)別的。

2、可迭代

books = Book.objects.all()
for book in books:
    print(book.name)

3、惰性查詢

查詢集 是惰性執(zhí)行的 —— 創(chuàng)建查詢集不會帶來任何數(shù)據(jù)庫的訪問。你可以將過濾器保持一整天,直到查詢集 需要求值時,Django 才會真正運(yùn)行這個查詢。

query_res = Book.objects.all()  # 不會查詢數(shù)據(jù)庫,終端也并無sql日志打印

print(query_res)  # 此時才會查詢數(shù)據(jù)庫,在開啟sql日志功能后,可以在終端看到執(zhí)行的原生sql

for book in query_res:
    print(book.name)  # 此時會再次查詢數(shù)據(jù)庫

一般來說,只有在“請求”查詢集 的結(jié)果時才會到數(shù)據(jù)庫中去獲取它們。當(dāng)你確實(shí)需要結(jié)果時,查詢集 通過訪問數(shù)據(jù)庫來求值

4、緩存機(jī)制

每個查詢集都包含一個緩存來最小化對數(shù)據(jù)庫的訪問。理解它是如何工作的將讓你編寫最高效的代碼。

在一個新創(chuàng)建的查詢集中,緩存為空。

當(dāng)我們首次對查詢集進(jìn)行求值時--同時發(fā)生數(shù)據(jù)庫查詢 ,Django 將查詢的結(jié)果保存到查詢集的緩存中并返回明確請求的結(jié)果。接下來對該查詢集 的求值將重用緩存的結(jié)果。

請牢記這個緩存行為,因?yàn)閷?em>查詢集使用不當(dāng)?shù)脑?,它會坑你的?/p>

例如,下面的兩條語句查出的都是所有的書籍,但是每條都創(chuàng)建了新的查詢集,然后各自對各自的查詢集求值,這存在兩大問題

? 1、相同的數(shù)據(jù)庫查詢將執(zhí)行兩次,顯然倍增了你的數(shù)據(jù)庫負(fù)載。

? 2、還有可能兩個結(jié)果列表并不包含相同的數(shù)據(jù)庫記錄,因?yàn)樵趦纱握埱笃陂g有可能有Article被添加進(jìn)來或刪除掉。

print([book.name for book in Book.objects.all()])
print([book.price for book in Book.objects.all()])

為了避免上述問題,只需保存查詢集并重新使用它,如下

# 1、先保存結(jié)果集
query_res = Book.objects.all()  

# 2、然后再對結(jié)果集進(jìn)行求值
print([book.name for book in query_res])  # 首次對查詢集求值,會查詢數(shù)據(jù)庫并緩存
print([book.price for book in query_res]) # 命中緩存,無需查詢數(shù)據(jù)庫

5、何時查詢集不會被緩存?

1、只有在對查詢集求值后才會緩存

如下操作都算是在對查詢集求值,它們會使得全部的查詢集被求值并填充到緩存中:

[book for book in query_res]
bool(query_res)
any_obj in query_res
list(query_res)

for obj in query_res:
    print('哪怕只遍歷一次,也算是對查詢集求值了,會緩存Book.objects.all()的所有結(jié)果')
    break

2、單純的打印結(jié)果集不算是對查詢集求值,所以不會被緩存,每次打印都會引發(fā)新的數(shù)據(jù)庫查詢

query_res = Book.objects.all()
print(query_res)  # 查詢數(shù)據(jù)庫
print(query_res)  # 查詢數(shù)據(jù)庫

3、使用切片或索引來限制查詢集也不算是對查詢集求值,所以也不會被緩存

query_res = Book.objects.all()
print(query_res[5])  # 查詢數(shù)據(jù)庫
print(query_res[3:5])  # 查詢數(shù)據(jù)庫

所以,我們可以先對查詢集求值,緩存好數(shù)據(jù)之后再進(jìn)行上述2和3的操作

query_res = Book.objects.all()

123 in query_res  # 對查詢集求值,會查詢數(shù)據(jù)庫并緩存

print(query_res)  # 命中緩存
print(query_res[5])  # 命中緩存
print(query_res[3:5])  # 命中緩存

6、exists()與iterator()方法

簡單的使用if語句進(jìn)行判斷也會完全執(zhí)行整個queryset并且把數(shù)據(jù)放入cache,如下

query_res = Book.objects.all()
if query_res:  # 查詢數(shù)據(jù)庫并緩存
    print('ok')

print(query_res)  # 命中緩存

若僅僅只需要判斷是否存在數(shù)據(jù),那么再把所有數(shù)據(jù)集合都查詢回來緩存將會極大地降低效率,此時我們可以使用exists(),只拿回來一條來判斷是否存在即可,需要拿所有,當(dāng)然也不會緩存

query_res = Book.objects.all()
if query_res.exists():  # 查詢數(shù)據(jù)庫,但不緩存
    # SELECT (1) AS `a` FROM `app01_book`  LIMIT 1
    print('ok')

print(query_res)  # 無法命中任何緩存,需要再次查詢數(shù)據(jù)庫

當(dāng)queryset非常巨大時,cache會成為問題。

處理成千上萬的記錄時,將它們一次裝入內(nèi)存是很浪費(fèi)的。更糟糕的是,巨大的queryset可能會鎖住系統(tǒng) 進(jìn)程,讓你的程序?yàn)l臨崩潰。要避免在遍歷數(shù)據(jù)的同時產(chǎn)生queryset cache,可以使用iterator()方法 來獲取數(shù)據(jù),iterator并不會產(chǎn)生緩存,處理完數(shù)據(jù)后就丟棄了,要想重新獲取數(shù)據(jù)得重新拿到iterator(),如下所示。

books = Book.objects.all().iterator()
# iterator()可以一次只從數(shù)據(jù)庫獲取少量數(shù)據(jù),這樣可以節(jié)省內(nèi)存

for obj in books:
    print(obj.name)
    
#強(qiáng)調(diào):再次遍歷沒有打印,因?yàn)榈饕呀?jīng)在上一次遍歷(next)到最后一次了,沒得遍歷了
for obj in books:
    print(obj.name)

注意:使用iterator()方法來防止生成cache,意味著遍歷同一個queryset時會重復(fù)執(zhí)行查詢。所以使用iterator()的時候要當(dāng)心,確保你的代碼在操作一個大的queryset時沒有重復(fù)執(zhí)行查詢。

總結(jié):
queryset的cache是用于減少程序?qū)?shù)據(jù)庫的查詢,在通常的使用下會保證只有在需要的時候才會查詢數(shù)據(jù)庫。 使用exists()和iterator()方法可以優(yōu)化程序?qū)?nèi)存的使用。不過,由于它們并不會生成queryset cache,可能 會造成額外的數(shù)據(jù)庫查詢。

二、跨表查詢優(yōu)化


select_related()

(1)簡單使用

對于一對一字段(OneToOneField)和外鍵字段(ForeignKey),可以使用select_related 來對QuerySet進(jìn)行優(yōu)化,select_related底層就是鏈表操作

簡單說,在對QuerySet使用select_related()函數(shù)后,Django會獲取相應(yīng)外鍵對應(yīng)的對象,從而在之后需要的時候不必再查詢數(shù)據(jù)庫了,例如

res = Book.objects.all()
for obj in res:
    print(obj.publish.name)  # 每次執(zhí)行該行代碼都會觸發(fā)新的sql執(zhí)行,效率極低
"""
底層會先去book表里查出所有的書籍id
然后每次for循環(huán)都會拿著一個書籍的id去publish表里查到對應(yīng)出版社的名字
"""    
    
    
# 優(yōu)化
res = Book.objects.select_related('publish')  

for obj in res:
    print(obj.publish.name)
"""
底層會先將Book與Publish對應(yīng)的兩張表連接成一張大表,然后一次性將大表的所有數(shù)據(jù)一次性封裝給查詢出來的對象
    此時對象無論是點(diǎn)擊book表的數(shù)據(jù)還是publish表的數(shù)據(jù)都無需再走額外的數(shù)據(jù)庫查詢了
"""
    
for obj in res:
    print(obj.publish.city)
"""
第二次for循環(huán)直接使用上述緩存即可
"""


但需要注意的是:select_related()括號內(nèi)只能放外鍵Foreignkey字段

因?yàn)橐粚Χ?、一對一關(guān)系均使用ForeignKey,而多對多則不是,所以select_related不支持多對多關(guān)系,若想優(yōu)化多對多的查詢請看下一小節(jié) (2)多外鍵查詢

如果一個模型中存在多個ForeignKey字段

res = Book.objects.select_related("publish")

此時我們查詢publish相關(guān)的時候,不會重復(fù)查詢,如下
for obj in res:
    print(obj.publish.name)

for obj in res:
    print(obj.publish.city)

但如果我們查詢的是Book內(nèi)的其他ForeignKey字段,還是會重新查詢
for obj in res:
    print(obj.other_fk.attr)
    
# 解決方案就是
res = Book.objects.select_related("publish","other_fk")

或者使用django1.7之后支持的鏈?zhǔn)讲僮?res = Book.objects.select_related("publish").select_related("detail")
(2)深層查詢

對于跨越了n張表的深層查詢, 依然需要查詢多次,例如下述代碼依然需要重復(fù)查詢兩次

article=models.Article.objects.select_related("blog").get(nid=1)
print(article.blog.user.username)  # 需要重復(fù)兩次進(jìn)行查詢

這是因?yàn)榈谝淮尾樵儧]有query到userInfo表,所以,修改如下:

article=models.Article.objects.select_related("blog__user").get(nid=1)
print(article.blog.user.username)
(3)總結(jié)

1、select_related主要針一對一和多對一關(guān)系進(jìn)行優(yōu)化。

2、select_related使用SQL的JOIN語句進(jìn)行優(yōu)化,通過減少SQL查詢的次數(shù)來進(jìn)行優(yōu)化、提高性能。

3、可以通過可變長參數(shù)指定需要select_related的字段名。也可以通過使用雙下劃線“__”連接字段名來實(shí)現(xiàn)指定的遞歸查詢。

4、沒有指定的字段不會緩存,沒有指定的深度不會緩存,如果要訪問的話Django會再次進(jìn)行SQL查詢。

5、也可以通過depth參數(shù)指定遞歸的深度,Django會自動緩存指定深度內(nèi)所有的字段。如果要訪問指定深度外的字段,Django會再次進(jìn)行SQL查詢。

6、也接受無參數(shù)的調(diào)用,Django會盡可能深的遞歸查詢所有的字段。但注意有Django遞歸的限制和性能的浪費(fèi)。

7、Django >= 1.7,鏈?zhǔn)秸{(diào)用的select_related相當(dāng)于使用可變長參數(shù)。Django < 1.7,鏈?zhǔn)秸{(diào)用會導(dǎo)致前邊的select_related失效,只保留最后一個。

prefetch_related()

對于多對多字段(ManyToManyField)和一對多字段,可以使用prefetch_related()來進(jìn)行優(yōu)化,prefetch_related()的底層就是子查詢,在使用時select_related與prefetch_related是沒有差別的,但是底層是有差別的

針對一對多字段

# 效果與select_related()一樣
res = Book.objects.prefetch_related('publish')  

for obj in res:
    print(obj.publish.name)
    
for obj in res:
    print(obj.publish.city)

針對多對多字段

books = Book.objects.prefetch_related('authors')  # 底層就是inner join
for book in books:
    for author in book.authors.all():
        print(book.name,author.name)

注意

鏈表與子查詢的效率不一定誰高誰低
鏈表有可能遇到很多張表,連在一起,會在耗費(fèi)很多時間在鏈表上
而子查詢,每次都一張表,分多步運(yùn)行,不需要鏈表,但步驟多了,有可能影響效率

通常情況下:
數(shù)據(jù)量少的話,建議用select_related
數(shù)據(jù)量比較多,建議用prefetch_related

三、only與defer

1、only

下例中,因?yàn)閞es是.all()得到的結(jié)果集合,所以obj.任意書籍對象自己的屬性,均不再走數(shù)據(jù)庫

res = Book.objects.all()

for obj in res: 
	print(obj.name)   # 不走數(shù)據(jù)庫查詢
	print(obj.price)  # 不走數(shù)據(jù)庫查詢

因?yàn)樯厦媸?all()所以拿到的結(jié)果集必然數(shù)據(jù)量大,如果我們只想要書的名字,可以使用.only()

res = Book.objects.only('name')

for obj in res:
   print(obj.name)  # .name不走數(shù)據(jù)庫 .其他字段會重新走數(shù)據(jù)庫查詢

2、defer

defer與only剛好相反,除了defer指定的屬性外,其他都不需要查數(shù)據(jù)庫

res = Book.objects.defer('name')  
for obj in res:
    print(obj.price)  # 除了.name之外,點(diǎn)出的屬性都不需要走數(shù)據(jù)庫

    # print(res)  # 打印res,會觸發(fā)多條sql的執(zhí)行來拿到所有的結(jié)果集,因?yàn)樵L問了除了name之外的屬性

四、整體插入

創(chuàng)建對象時,盡可能使用bulk_create()來減少SQL查詢的數(shù)量。例如:

Entry.objects.bulk_create([
    Entry(headline="Python 3.0 Released"),
    Entry(headline="Python 3.1 Planned")
])

...更優(yōu)于:

Entry.objects.create(headline="Python 3.0 Released")
Entry.objects.create(headline="Python 3.1 Planned")

注意該方法有很多注意事項(xiàng),所以確保它適用于你的情況。

這也可以用在ManyToManyFields中,所以:

my_band.members.add(me, my_friend)

...更優(yōu)于:

my_band.members.add(me)
my_band.members.add(my_friend)

...其中Bands和Artists具有多對多關(guān)聯(lián)。文章來源地址http://www.zghlxwxcb.cn/news/detail-519198.html

到了這里,關(guān)于Django之QuerySet對象與查詢優(yōu)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Python Web開發(fā):Django與Flask框架

    Django和Flask都是Python中非常受歡迎的Web開發(fā)框架。雖然它們都是用于構(gòu)建Web應(yīng)用程序的工具,但它們在設(shè)計理念、使用方式和適用場景等方面存在一些差異。 Django Django是一個“大而全”的框架,遵循MVC設(shè)計模式。它內(nèi)置了很多功能,如ORM(對象關(guān)系映射)、模板引擎、表單處

    2024年02月22日
    瀏覽(20)
  • Python web 框架對比:Flask vs Django

    哈嘍大家好,我是咸魚 今天我們從幾個方面來比較一些現(xiàn)在流行的兩個 python web 框架——Flask 和 Django,突出它們的主要特性、優(yōu)缺點(diǎn)和簡單案例 到最后,大家將更好地了解哪個框架更適合自己的特定需求 參考鏈接:https://djangocentral.com/flask-vs-django-selecting-the-perfect-python-web

    2024年02月11日
    瀏覽(32)
  • Django:用于輕松安全 Web 開發(fā)的高級 Python Web 框架

    Django:用于輕松安全 Web 開發(fā)的高級 Python Web 框架

    Django是一種高級 Python Web 框架,近年來在開發(fā)人員中廣受歡迎。Django 專注于簡單性、安全性和可擴(kuò)展性,使開發(fā)人員可以輕松構(gòu)建和部署強(qiáng)大的 Web 應(yīng)用程序。在這份綜合指南中,我們將仔細(xì)研究是什么讓 Django 成為 Web 開發(fā)的絕佳選擇,并詳細(xì)探討其主要特性和功能。 1. 安

    2024年02月15日
    瀏覽(26)
  • Python web實(shí)戰(zhàn)之Django性能優(yōu)化最佳實(shí)踐詳解

    大家好!今天分享如何優(yōu)化使用Django應(yīng)用的性能,使其在高并發(fā)、大數(shù)據(jù)量的情況下能夠保持良好的性能。 數(shù)據(jù)庫查詢是Web應(yīng)用中常見的性能瓶頸之一。 1.1 使用select_related和prefetch_related 在Django中,可以使用 select_related 和 prefetch_related 方法來優(yōu)化數(shù)據(jù)庫查詢。這兩個方法可

    2024年02月11日
    瀏覽(88)
  • Python 框架學(xué)習(xí) Django篇 (八) 代碼優(yōu)化、數(shù)據(jù)庫冗余處理

    Python 框架學(xué)習(xí) Django篇 (八) 代碼優(yōu)化、數(shù)據(jù)庫冗余處理

    我們開發(fā)軟件系統(tǒng)的時候,需要不斷的反思我們代碼里面是否有可以優(yōu)化的地方。而優(yōu)化的重點(diǎn)之一,就是把冗余的代碼優(yōu)化為可以復(fù)用的庫。我們在前面編寫了一些功能,但是其中存在很多冗余的方法 打開這3個文件我們可以看到他們的入口函數(shù)dispatcher? 實(shí)際的代碼相似度

    2024年02月06日
    瀏覽(24)
  • Python Web框架:Django、Flask和FastAPI巔峰對決

    Python Web框架:Django、Flask和FastAPI巔峰對決

    今天,我們將深入探討Python Web框架的三巨頭:Django、Flask和FastAPI。無論你是Python小白還是老司機(jī),本文都會為你解惑,帶你領(lǐng)略這三者的魅力。廢話不多說,讓我們開始這場終極對比! Django,這個強(qiáng)大的全能型框架被譽(yù)為“Web開發(fā)的瑞士軍刀”,無愧是大型項(xiàng)目的不二之選

    2024年02月12日
    瀏覽(50)
  • Python 開源 Web 應(yīng)用框架 Django 簡介 應(yīng)用場景 優(yōu)勢 不足

    Django 是一個開放源代碼的 Web 應(yīng)用框架,使用 Python 編程語言編寫。它遵循了 “MTV”(模型-模板-視圖)的設(shè)計模式,旨在幫助開發(fā)者快速構(gòu)建高質(zhì)量、易維護(hù)的 Web 應(yīng)用程序。 Web 應(yīng)用開發(fā) :Django 適用于構(gòu)建各種規(guī)模的 Web 應(yīng)用程序,包括企業(yè)級網(wǎng)站、社交網(wǎng)絡(luò)、內(nèi)容管理系

    2024年01月19日
    瀏覽(32)
  • Python Web開發(fā)記錄 Day7:Django(Web框架) part 1

    Python Web開發(fā)記錄 Day7:Django(Web框架) part 1

    名人說:莫道桑榆晚,為霞尚滿天?!獎⒂礤a(劉夢得,詩豪) 創(chuàng)作者:Code_流蘇(CSDN) (一個喜歡古詩詞和編程的Coder??) 在當(dāng)今迅速發(fā)展的互聯(lián)網(wǎng)時代,開發(fā)高效、安全且可擴(kuò)展的網(wǎng)站變得越來越重要。Python語言因其簡潔的語法和強(qiáng)大的功能而廣受歡迎,而Django則是P

    2024年03月11日
    瀏覽(19)
  • 試玩python的web框架 flask、fastapi、tornado、django

    試玩python的web框架 flask、fastapi、tornado、django

    先解決一下IDEA使用遠(yuǎn)程解釋器,本地代碼編輯無法代碼提示問題 常用的4個Python Web框架對比 注意 1.這里使用linux 192.168.72.126上遠(yuǎn)程解釋器,需要 /usr/bin/pip3 install flask ,host參數(shù)不要使用localhost/127.0.0.1,即只監(jiān)聽本地的訪問,會導(dǎo)致windows無法訪問到flask app 2.運(yùn)行方式增加main方法

    2024年02月17日
    瀏覽(22)
  • Django基礎(chǔ)入門?:Django 對象查詢詳解,分組聚合

    ??????個人簡介:以山河作禮。 ??????: Python領(lǐng)域新星創(chuàng)作者,CSDN實(shí)力新星認(rèn)證,阿里云社區(qū)專家博主,CSDN內(nèi)容合伙人 ????:Web全棧開發(fā)專欄:《Web全棧開發(fā)》免費(fèi)專欄,歡迎閱讀! ????: 文章末尾掃描二維碼可以加入粉絲交流群,不定期免費(fèi)送書。 F對象查詢與

    2024年02月12日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包