真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

探索DjangoORM的極限

我最近在2019年歐洲D(zhuǎn)jango大會(huì)(https://2019.djangocon.eu/? )上發(fā)表了一場(chǎng)關(guān)于Django?ORM的演講。在這次演講中,我展示了使用Django ORM進(jìn)行復(fù)雜查詢時(shí)可以使用的各種技術(shù)。這篇文章將部分總結(jié)這次演講,但我也會(huì)擴(kuò)展和添加我無法在30分鐘內(nèi)完成的額外的內(nèi)容。

在永清等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作按需策劃,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),網(wǎng)絡(luò)營銷推廣,外貿(mào)網(wǎng)站建設(shè),永清網(wǎng)站建設(shè)費(fèi)用合理。

探索Django ORM的極限

首先,ORM代表對(duì)象關(guān)系映射,是一個(gè)幫助你處理數(shù)據(jù)庫的工具。Django ORM提供了一個(gè)Python接口,用于處理數(shù)據(jù)庫中的數(shù)據(jù)。它對(duì)你有兩大好處: 它通過使用模型定義和遷移來幫助你設(shè)置和維護(hù)數(shù)據(jù)庫結(jié)構(gòu),并通過管理器和查詢集幫助你編寫針對(duì)數(shù)據(jù)庫的查詢。

Django ORM沒有開放一個(gè)接口來讓你編寫自定義SQL。該接口只關(guān)注你定義的模型。這使得使用ORM非常容易,但也使得使用ORM編寫某些查詢更困難——甚至不可能。

示例模型

在本文中,我將在大多數(shù)例子中使用下面定義的模型:

探索Django ORM的極限

自定義管理器和查詢集

我們?cè)贙olonial.no上大量使用的東西是為我們的模型定制的Manager(管理器)和QuerySet(查詢集)。在這里,你可以保持與你的模型相關(guān)的可重用邏輯。例如,我們可以在訂單QuerySet中添加一個(gè)方法,該方法給出一個(gè)未發(fā)貨訂單的列表:

探索Django ORM的極限

類似地,我們可以創(chuàng)建一個(gè)自定義管理器,例如使用一個(gè)幫助器方法來簡化新訂單的創(chuàng)建:

探索Django ORM的極限

為了設(shè)置order(訂單)模型的默認(rèn)管理器,我們?cè)O(shè)置了objects屬性:

探索Django ORM的極限

檢查QuerySet

另一個(gè)需要知道的有用技巧是如何檢查一個(gè)QuerySet。假設(shè)你想知道為什么一個(gè)特定的QuerySet不能準(zhǔn)確地返回你所期望的結(jié)果。那么,打開一個(gè)shell并實(shí)際檢查數(shù)據(jù)庫中正在運(yùn)行的查詢是非常有用的。這里我特別想強(qiáng)調(diào)兩件事: 如何查看某個(gè)QuerySet生成的SQL查詢,以及如何在數(shù)據(jù)庫中運(yùn)行一個(gè)EXPLAIN查詢。

探索Django ORM的極限

你還可以訪問Django中的數(shù)據(jù)庫連接包裝器,并檢查在該連接上運(yùn)行的最后查詢:

探索Django ORM的極限

這將給出執(zhí)行的SQL語句的列表和每個(gè)查詢的運(yùn)行時(shí)間。

避免額外的查詢

當(dāng)使用Django ORM時(shí),很容易出現(xiàn)視圖生成過多查詢的情況。如果你有一個(gè)相關(guān)的模型,并對(duì)QuerySet中的每個(gè)實(shí)例進(jìn)行訪問,則默認(rèn)行為是一次獲取一個(gè)相關(guān)的模型。

探索Django ORM的極限

為了避免這種情況,你可以使用select_related和prefetch_related。它們有著非常相似的名字和行為。第一個(gè)用于獲取數(shù)據(jù)庫中的對(duì)象,這些對(duì)象每一個(gè)都和一行相關(guān)聯(lián),并生成一個(gè)JOIN查詢,其中所有相關(guān)對(duì)象都在一個(gè)SQL查詢中獲取。當(dāng)你的數(shù)據(jù)庫中每一行擁有多個(gè)相關(guān)對(duì)象時(shí),你可以使用第二種方法。它將首先獲取原始QuerySet的所有對(duì)象,而不是創(chuàng)建一個(gè)JOIN查詢。然后,它將運(yùn)行第二個(gè)查詢來獲取所有相關(guān)對(duì)象,然后在Python中而不是數(shù)據(jù)庫中來連接它們。因此,通過在我們的Orders QuerySet中添加以下兩行代碼,我們最終會(huì)得到兩個(gè)查詢:

探索Django ORM的極限

但是請(qǐng)注意,你不應(yīng)該盲目地優(yōu)化。有時(shí)運(yùn)行兩個(gè)或多個(gè)查詢比運(yùn)行一個(gè)大型查詢更快。所以,在研究性能問題時(shí),請(qǐng)記住這一點(diǎn)。

避免競爭條件

如果你正在處理數(shù)據(jù)庫中的對(duì)象,這些對(duì)象可以通過多個(gè)請(qǐng)求并發(fā)地進(jìn)行修改,那么你需要確保這些更改是按照正確的順序應(yīng)用的。這可能很重要,例如,如果我們?cè)诋a(chǎn)品模型上保持庫存數(shù)量,我們希望確保該數(shù)量正確地遞增和遞減。對(duì)此的一種解決方案是,當(dāng)我們從數(shù)據(jù)庫中獲取對(duì)象時(shí),對(duì)數(shù)據(jù)庫中的行進(jìn)行鎖定。這樣,我們就可以保證不允許其他請(qǐng)求獲取和修改相同的行。

探索Django ORM的極限

就性能而言,這是一個(gè)相當(dāng)繁重的解決方案,在事務(wù)完成之前,不允許任何其他數(shù)據(jù)庫連接訪問這一行。在對(duì)你的數(shù)據(jù)進(jìn)行建模時(shí),請(qǐng)記住這一點(diǎn)。也許你可以通過對(duì)數(shù)據(jù)進(jìn)行不同的建模來提前避免這個(gè)問題?

子查詢

當(dāng)你需要從另一個(gè)表(有時(shí)甚至是同一個(gè)表)獲取一些數(shù)據(jù),但在Django模型中又沒有任何直接相關(guān)字段時(shí),子查詢非常有用。在這種情況下,Django目前不允許執(zhí)行連接。另一個(gè)用例是當(dāng)要獲取或搜索的數(shù)據(jù)量太大,執(zhí)行普通連接的速度太慢的時(shí)候。

我的第一個(gè)示例向你展示了如何將來自一行的單個(gè)值注釋到一個(gè)QuerySet上。在這個(gè)例子中,我用客戶下最后一筆訂單的時(shí)間來標(biāo)注每個(gè)客戶對(duì)象:

探索Django ORM的極限

這里兩個(gè)有趣的部分是Subquery和OuterRef類。第一個(gè)是一個(gè)包裝器,它接受一個(gè)普通的QuerySet并將其作為子查詢嵌入到另一個(gè)QuerySet中。OuterRef類用于引用嵌入子查詢的QuerySet中的字段。在本例中,我們使用它進(jìn)行篩選,以便只獲得屬于當(dāng)前行客戶的訂單。然后按日期排序,只選擇日期列,然后只返回第一個(gè)結(jié)果。

除了從另一個(gè)QuerySet中選擇一個(gè)特定的值,我們還可以使用Exists類檢查另一個(gè)對(duì)象是否存在:

探索Django ORM的極限

如果你對(duì)選擇結(jié)果不感興趣,而只是想過濾,那么Django目前不支持這種方法。這很不幸,因?yàn)檫@會(huì)導(dǎo)致查詢速度變慢。幸運(yùn)的是,目前有一個(gè)開放的推送請(qǐng)求(https://github.com/django/django/pull/8119? )來解決這個(gè)問題,所以在Django的未來版本中,很可能會(huì)支持這個(gè)功能。

上面的兩個(gè)例子只是從數(shù)據(jù)庫中的另一個(gè)對(duì)象中選擇一個(gè)值,但是我們也可以使用聚合的結(jié)果運(yùn)行更復(fù)雜的查詢。下面是如何聚合子查詢中匹配行的總和的例子:

探索Django ORM的極限

這里我們要做的第一件事是過濾出我們感興趣的一組行。然后,我們使用values_list只選擇年和月的結(jié)果。當(dāng)我們?cè)诖酥笫褂镁酆虾瘮?shù)進(jìn)行注釋時(shí),查詢結(jié)果將根據(jù)所選的行進(jìn)行分組,這些行對(duì)于匹配篩選器的任何行來說都是惟一的。然后,我們只選擇聚合值并將其注釋到外部QuerySet上。

在本例中,我們可以使用默認(rèn)可用的Django基本類型生成這個(gè)查詢,但是如果我們想要聚合一些行,而這些行中沒有惟一的東西可以進(jìn)行分組,該怎么辦呢?我們不能在子查詢中使用aggregate,因?yàn)樗鼤?huì)立即運(yùn)行數(shù)據(jù)庫查詢。相反,我們需要做的是在ORM周圍使用一些技巧。假設(shè)我們有這張訂單表,并想計(jì)算所有周六和周日的總銷售額:

探索Django ORM的極限

我們沒有任何獨(dú)特的東西可以用來分組,但是我們可以嘗試刪除values_list調(diào)用。不幸的是,這并沒有帶給我們想要的結(jié)果:

探索Django ORM的極限

這不起作用的原因是,每當(dāng)我們使用帶注釋的Django向QuerySet添加一個(gè)聚合時(shí),都會(huì)添加一個(gè)group by語句,在本例Order.pk中,默認(rèn)情況下,這是QuerySet模型的主鍵。結(jié)果是我們只得到第一個(gè)訂單的和。相反,我們必須使用一個(gè)不繼承自Aggregate類的數(shù)據(jù)庫函數(shù)。在Django中,SUM SQL函數(shù)只能作為一個(gè)聚合子類使用,但是我們可以相對(duì)容易地通過直接使用Func類來解決這個(gè)問題:

探索Django ORM的極限

雖然這不是特別漂亮,但它確實(shí)給出了我們想要的結(jié)果。雖然我并不總是建議使用這個(gè)方法,但是知道它是一個(gè)可用選項(xiàng)是很有用的。

自定義約束和索引

Django在很長一段時(shí)間內(nèi)都支持唯一性約束,但是只有當(dāng)你希望字段的組合在表中的所有行中都是唯一的時(shí)候才需要使用它:

探索Django ORM的極限

我們還可以選擇向表中的某些列添加額外的索引,但同樣只針對(duì)整個(gè)表。從Django2.2開始,我們可以更好地控制如何創(chuàng)建唯一性約束和自定義索引。我們現(xiàn)在可以指定一個(gè)唯一性約束只檢查表中所有行的一個(gè)子集:

探索Django ORM的極限

在本例中,我們將每個(gè)用戶限制為只有一個(gè)未發(fā)貨訂單。雖然這可能是一個(gè)相對(duì)簡單的例子,但是條件唯一性約束給了我們很大的靈活性。假設(shè)我們有一個(gè)數(shù)據(jù)庫表,其中我們希望只允許一行具有NULL值。因?yàn)樵赟QL中NULL是不等于NULL的,所以每一行都被認(rèn)為是惟一的,我們不能使用一個(gè)普通的unique=True或unique_together來限制這一點(diǎn)。但是,我們可以添加一個(gè)約束,在字段為NULL的情況下檢查它是否是唯一的。

我們還可以創(chuàng)建自定義檢查約束。這類似于Django中的字段驗(yàn)證器,但是它們是由數(shù)據(jù)庫執(zhí)行的。這可以保護(hù)我們不受Python代碼中的bug的影響,該檢查甚至可以在bulk_create和類似的平臺(tái)上運(yùn)行,如果你從另一個(gè)非django項(xiàng)目訪問相同的數(shù)據(jù)庫時(shí),該檢查也會(huì)運(yùn)行。例如,我們可以創(chuàng)建一個(gè)檢查約束來驗(yàn)證給定的月份數(shù)字是否有效:

探索Django ORM的極限

類似地,我們可以創(chuàng)建自定義的部分索引,它只覆蓋表的一部分。如果只查詢表的一個(gè)子集,或者表的很大一部分包含空NULL值時(shí),這將非常有用。在我們的示例模型中,在準(zhǔn)備發(fā)貨時(shí),我們很可能會(huì)頻繁地訪問未發(fā)貨的訂單。下面是如何創(chuàng)建只包含未發(fā)貨訂單的索引的示例。這將有助于加快我們最常用的數(shù)據(jù)庫查詢:

探索Django ORM的極限

窗口函數(shù)

在Django2.0中,使用DjangoORM運(yùn)行窗口函數(shù)進(jìn)行查詢得到了支持。這將在查詢中生成一個(gè)OVER語句來查看行分區(qū)。這是很有用的,例如,如果我們想查找某個(gè)客戶的上一個(gè)訂單,查看該客戶上一次下訂單的時(shí)間,或者計(jì)算來自該客戶的訂單總和。下面是一個(gè)示例,演示如何使用來自同一客戶的上一個(gè)訂單的id來注釋一個(gè)訂單的QuerySet:

探索Django ORM的極限

我們使用Window類來生成OVER語句,然后生成Lag來從第n行中選擇一個(gè)值,在本例中是前一行,因?yàn)槲覀儗指定為1。

不幸的是,由于SQL標(biāo)準(zhǔn)限制,我們不能在窗口函數(shù)上進(jìn)行過濾。但是,這可以通過使用通用表表達(dá)式(CTEs)來補(bǔ)救,但是Django目前不支持這種方法。

使用自定義數(shù)據(jù)庫函數(shù)進(jìn)行擴(kuò)展

有時(shí)候Django ORM不允許你做你想做的事情。在許多情況下,實(shí)際上可以通過擴(kuò)展內(nèi)置基本類型來添加自己的功能。例如,如果你想使用Django沒有公開的自定義SQL函數(shù),這可以通過子類化Func函數(shù)來輕松添加:

探索Django ORM的極限

這就是所需的全部。現(xiàn)在,我們可以像使用任何Django公開的SQL函數(shù)一樣使用它。

我們也可以用更復(fù)雜的函數(shù)來擴(kuò)展。假設(shè)我們用單獨(dú)的日期和時(shí)間列建立了一個(gè)模型,但是需要將其與使用日期時(shí)間的另一個(gè)表進(jìn)行比較。我們可以通過實(shí)現(xiàn)一個(gè)自定義函數(shù)來實(shí)現(xiàn)這一點(diǎn)。沒有跨數(shù)據(jù)庫的通用方法可以做到這一點(diǎn),但是Django允許我們?yōu)橄胍С值拿總€(gè)數(shù)據(jù)庫分別實(shí)現(xiàn)這一點(diǎn)。下面是用于PostgreSQL和Sqlite的例子:

探索Django ORM的極限

接著,我們就可以使用這個(gè)函數(shù)來在QuerySet上注釋一個(gè)日期時(shí)間:

探索Django ORM的極限

運(yùn)行自定義SQL

如果我們想要做的事情使用Django提供給我們的功能不能直接實(shí)現(xiàn),我們有時(shí)可以直接編寫自定義SQL。Django提供了多種實(shí)現(xiàn)方法。例如,我們可以用一個(gè)自定義SQL表達(dá)式來對(duì)QuerySet進(jìn)行注解:

探索Django ORM的極限

我們也可以用QuerySet的extra方法來做同樣的事情:

探索Django ORM的極限

extra有很多的選項(xiàng),所以請(qǐng)查閱文檔。

如果我們想自己編寫整個(gè)SQL查詢,這也是一個(gè)選項(xiàng)。返回的列將按名稱映射到模型字段。任何與現(xiàn)有字段名不匹配的列都將作為注解字段添加。

探索Django ORM的極限

如果我們不想返回模型對(duì)象,我們也可以直接在數(shù)據(jù)庫游標(biāo)上運(yùn)行自定義SQL查詢:

探索Django ORM的極限

這將返回一個(gè)包含列的元組。

自定義遷移

向Django添加額外數(shù)據(jù)庫功能的另一種方法是編寫自定義遷移。在Django獲得對(duì)自定義約束和索引的支持之前,在自定義遷移中使用RunSQL是向數(shù)據(jù)庫添加這類選項(xiàng)的一種方法。另一種可能性是使用RunSQL向數(shù)據(jù)庫添加自定義視圖,并將其作為一個(gè)模型公開:

探索Django ORM的極限

自定義遷移的另一個(gè)用例是數(shù)據(jù)遷移。如果你的數(shù)據(jù)庫中有一組始終可用的初始數(shù)據(jù),那你可以使用數(shù)據(jù)遷移將其添加到自定義遷移中。如果你在運(yùn)行測(cè)試時(shí)使用遷移,那么相同的初始數(shù)據(jù)集也將在測(cè)試中可用。要做到這一點(diǎn),你可以在你的遷移文件中使用RunPython類:

探索Django ORM的極限

我將向你展示的與遷移相關(guān)的最后一個(gè)基本類型是SeparateDatabaseAndState類。Django在運(yùn)行遷移時(shí),除了數(shù)據(jù)庫中的實(shí)際狀態(tài)外,還保留一個(gè)內(nèi)部狀態(tài)來表示模型的預(yù)期布局等。使用這個(gè)類,你可以分別修改這兩個(gè)狀態(tài)。如果你需要將這兩者分開,例如出于高可用性的原因,這個(gè)類將非常有用。這就需要用到列表,一個(gè)在內(nèi)部狀態(tài)下執(zhí)行操作,一個(gè)在數(shù)據(jù)庫中執(zhí)行操作:

探索Django ORM的極限

總結(jié)

我希望本文為你提供了一些關(guān)于在使用Django ORM時(shí)如何優(yōu)化或改進(jìn)數(shù)據(jù)庫查詢的想法。

這絕不是一個(gè)完整的列表,所以請(qǐng)查看Django文檔(https://docs.djangoproject.com/? )獲取詳細(xì)內(nèi)容。我建議從QuerySet API reference(https://docs.djangoproject.com/en/2.2/ref/models/querysets/? 0部分開始,然后從那里跟隨鏈接去查看可用的API。

英文原文:https://qiniumedia.freelycode.com/vcdn/1/%E4%BC%98%E8%B4%A8%E6%96%87%E7%AB%A0%E9%95%BF%E5%9B%BE3/pushing-django-orm-to-its-limit.pdf?

譯者:憂郁的紅秋褲


網(wǎng)站欄目:探索DjangoORM的極限
本文來源:http://weahome.cn/article/googoe.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部