為Django開發者介紹SQLAIchemy ORM

2021-12-31 Python程式設計師

我經常在工作和個人項目中使用Django。但是在最近的工作項目中,我有機會使用Flask和SQLAlchemy。所以我必須學點新東西。

Flask很簡單,因為它是一個微框架,裡面沒有很多東西。然而,理解SQLAlchemy以及如何使用它比我預期的要困難得多。

在本文中,我試圖通過一些示例來展示Django ORM和SQLAlchemy之間的主要區別,這些示例演示了如何在Django中執行某些操作,以及如何在SQLAlchemy中執行相同的操作。如果您出於任何原因試圖切換到SQLAlchemy,我希望它對您有用。

在深入討論示例和Django ORM與SQLAlchemy之間的區別之前,讓我們先從理解事務開始,因為如果您想使用SQLAlchemy編寫任何內容,那麼事務是一個非常重要的概念。

事務

將事務視為確保多個資料庫操作(插入、刪除、……)作為一個組,要麼一起成功,要麼一起失敗的一種方法。

啟動事務時,我們記錄資料庫的當前狀態,然後執行SQL語句。如果所有這些語句都成功,則提交事務。提交之後,所有更改都將持久化到資料庫中,並對其他事務可見。

但是,如果一個或多個語句失敗,我們將捕獲異常並回滾任何成功的語句。

Django和SQLAlchemy中的事務

我們在Django和SQLAlchemy中處理事務的方式是不同的。

在Django中,我們很少考慮事務。因為Django的默認行為是在自動提交模式下運行,這意味著每個SQL語句都被包裝到它自己的事務中,這個事務將根據SQL語句成功與否自動提交或回滾。


但在SQLAlchemy中,我們有Session對象。會話是SQLAlchemy與資料庫交互的方式。它允許您累積多個更改,然後發出commit命令,該命令將自動將所有更改作為一個單元寫入資料庫。這種模式也稱為工作單元:


當提交引發一個異常,你想要處理它,你應該手動回滾事務,這樣你的應用程式才能繼續正常運行:

Django和SQLAlchemy中的原子性

現在,當您了解了Django和SQLAlchemy處理事務的區別之後,您應該會看到這兩種方法的優缺點。

在自動提交模式下運行的優點是,它使得使用這個ORM更容易理解和編寫代碼。缺點是,如果你有多個查詢,其中一個成功,另一個失敗,那麼你的資料庫就有損壞的風險:


解決風險的方法是對資料庫原子進行查詢。原子性意味著您在一個事務中所做的事情作為一個單元進行或失敗。如果代碼塊成功完成,則將更改提交給資料庫。如果出現異常,則回滾更改。

這就是session在SQLAlchemy中的作用。在Django中,我們可以使用原子函數實現原子性:


這兩種Transfer要麼都將被添加到資料庫中,要麼都不添加。

Django和SQLAlchemy中的模型

在Django和SQLAlchemy中定義模型時,您將立即看到的主要區別是,在SQLAlchemy中必須顯式地定義模型。但是在Django中,很多事情都是在底層完成的。

例如,讓我們看看如何在Django和SQLAlchemy中定義具有不同關係的模型。

在Django中:


同樣的事情在SQLAlchemy:


讓我們來討論一下主要的區別。

Django的模型。默認情況下,Model類創建一個自動遞增的整數主鍵。在SQLAlchemy我們必須明確定義:


另一個區別是,在SQLAlchemy中,我們必須自己對關係建模,比如一對多、多對多和一對一。在Django中更簡單,因為它為您處理關係。

例如,如果我們想在SQLAlchemy中創建一對多的關係,我們應該首先定義一個列:


然後聲明兩個模型之間的關係:


注意,當我們定義一個外鍵時,我們指向聊天表的id列。但是當我們定義模型之間的關係時,我們不是指向表,而是指向Chat模型。

backref參數自動聲明反向關係。lazy= ' dynamic '創建了一個動態關係,這意味著當我們訪問chat時。消息,SQLAlchemy將返回一個查詢對象,我們可以進一步過濾:


如果我們不指定lazy參數,默認值將是' select '。它的工作方式不同。當我們首次使用chat.messages時,SQLAlchemy會向資料庫發送一個查詢,獲取所有相關的消息,並返回一個列表:

Django和SQLAlchemy中的查詢

當我們想要在Django中過濾查詢時,我們使用的關鍵字參數的格式是column=value或column_operation =value:


在SQLAlchemy中,我們使用模型表達式進行過濾:


說到連接,在Django中更簡單,因為它為我們處理連接:


在SQLAlchemy我們必須明確:


SQLAlchemy會自動嘗試查找要連接的內容,但是我們可以明確地將其指定為要連接的第二個參數:


 

大多數情況下,它在不指定第二個參數的情況下也能工作。但是,如果模型之間有很多關係,有時它會以您不希望的方式執行自動連接。所以要小心。

現在讓我們看看如何重用查詢。例如,如果我們需要獲得有未讀消息的對話框(與2個參與者的聊天),該怎麼辦?或者我們想要檢查一個特定的聊天是否是一個對話框,以及它是否有未讀的消息。我們不想在不同的地方重複粘貼代碼。記得擦乾(不要重複)。

在Django中,我們可以為聊天模型創建一個自定義的QuerySet和屬性:


如何使用:


在SQLAlchemy中,我們可以使用混合屬性或方法使代碼可重用:

混合屬性意味著該屬性既可以在類級使用,也可以在實例級使用:


混合方法也可以在實例級和類級使用。不同之處在於,如果需要,我們可以將參數傳遞給方法。

N + 1問題

讓我們來討論一個無論使用Django還是Flask都會遇到的問題:假設您需要從資料庫中提取每個聊天的消息,然後顯示消息文本和關於消息發送者的信息:


為了顯示所有這些信息,我們將向資料庫發送多少個查詢?由於Django比較懶,所以在編寫chat .object .all()時,它只將聊天信息提取到內存中。它不提取關於消息的信息,當然,也不提取關於消息發送者的信息。

因此,如果我們有2個聊天,每個聊天有10條消息,那麼它將需要23個查詢。首先,我們從資料庫中提取所有聊天記錄(1 db請求)。對於每個聊天,我們提取所有消息(另一個2 db請求)。最後,對於每條消息,我們提取其發送者的信息(2個聊天* 10條消息)。1 + 2 + 2 * 10 = 23

我們可以做的是使用prefetch_related函數將需要的信息預加載到內存中:


我們總共只有3個查詢。一個用於聊天,一個用於消息,一個用於用戶(發送者)。

我們可以在SQLAlchemy做類似的事情:


我們使用joinedload將sender預加載到內存中。正如您所看到的,我們沒有預先加載聊天消息。問題是您不能預加載lazy= ' dynamic '關係,因為它產生的是查詢,而不是集合(列表)。因此,在為您的模型定義和使用lazy= ' dynamic '關係時要小心,因為它會導致對資料庫的大量查詢。然而,沒有什麼可以阻止你定義兩個關係到同一個模型:


我建議您使用默認情況下返回列表的關係,但是當您確實需要動態關係來過濾嵌套數據時,您可以輕鬆地將其添加到模型中。

另外,請記住,即使不使用動態關係,您也可以過濾嵌套的關係。contains_eager能幫你做到:


首先,我們加載具有未讀消息的聊天。然後我們預加載聊天-信息的關係。預加載後,此關係將生成未讀消息列表。

結論

我們已經了解了最流行的Python ORM: SQLAlchemy和Django ORM。從我的經驗來看,Django ORM更容易學習和使用,但是SQLAlchemy為您提供了更大的靈活性,而且可能更適合大型應用程式。

如果您是初學者,並且正在嘗試為下一個項目Django或Flask + SQLAlchemy選擇使用什麼,我強烈建議您繼續使用Django。最初,Flask可能看起來很簡單,但是當您開始構建能夠解決實際問題的應用程式時,使用起來就比Django更有挑戰性了。此外,Django的生態系統更大。它有更多的開源庫和教程。


如果您不是初學者,並且正在嘗試構建微服務,那麼Flask + SQLAlchemy可能是更好的選擇。但如果你已經到了那一步,你自己應該就知道如何對框架進行取捨了。

英文原文:https://apirobot.me/posts/introduction-to-sqlalchemy-orm-for-django-developers
譯者: Yang

相關焦點

  • Django ORM 簡介
    這是為這個教程事先準備的 models.py:from django.db import modelsclass Author(models.Model):    name = models.CharField(max_length=100)    def __str__(self):        return
  • 探索Django ORM的極限
    幸運的是,目前有一個開放的推送請求(https://github.com/django/django/pull/8119  )來解決這個問題,所以在Django的未來版本中,很可能會支持這個功能。上面的兩個例子只是從資料庫中的另一個對象中選擇一個值,但是我們也可以使用聚合的結果運行更複雜的查詢。下面是如何聚合子查詢中匹配行的總和的例子:
  • django模型使用
    之前寫過一篇django的模型關係,今天騰出時間把上篇的兄弟篇補上,來學習下django orm, 利用django的模型可以很方便的對資料庫進行操作
  • 一篇文章帶你了解Django ORM操作(基礎篇)
    Django models代碼from django.db import modelsclass Author(models.Model): name前置導入import osimport djangoos.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_orm_demo.settings")django.setup()from web import models查詢所有(all)
  • 優化Django ORM查詢
    為了解決這個問題,Django提供了一種清理它們的方法:>>> from django.db import reset_queries>>> reset_queries()2. shell_plus –print-sqldjango-extensions項目很棒,
  • Django ORM的簡單總結
    對於QuerysetAPI的內容,如果看文檔有非常多的解釋和介紹,很難抓到重點,我就從我的認知來梳理一下。1.##做聚合結算,需要導入Count,使用annotate>>> from django.db.models import Count>>> dept.objects.all().values('dname').annotate(count=Count('dname')).values('dname','count')
  • Django 博客教程:前言和環境安裝(連載一)
    我們以互相分享各自所學的 django 知識並且利用這些知識合作開發一個項目的形式,順利地開發了一個 django 個人博客和一個 django 社區應用,並且還發布了一套 django 博客教程,但是由於當時自己也是學習 django 不久,對 django 的掌握程度還很不夠,教程也比較簡略,對想學習 django 的開發者依然不夠友好。
  • 一篇文章帶你了解Django ORM操作(高端篇)
    代碼from django.db.models import Avg,Sumprice = models.Book.objects.all().aggregate(所有書總價格=Sum代碼from django.db.models import Countret = models.Book.objects.values("publish_id").annotate
  • python測試開發django-76.ORM查詢之Q查詢
    前言ORM 使用 filter() 查詢的時候,當有多個條件的時候,可以使用 Q 查詢Q 查詢 or如有個 Card 模型,表裡面有以下數據查詢 card_user 名稱為>select * from yoyo_card where card_user = 'YOYO' or 'yoyo';filter() 查詢,查詢或關係MyDjango>python manage.py shell>>> from yoyo.models import Card>>> from django.db.models
  • 深入理解Django時區及naive datetime object和aware datetime object的區別
    引言相信使用Django的各位開發者在存儲時間的時候經常會遇到這樣子的錯誤:RuntimeWarning: DateTimeField received a naive datetime while time zone support
  • Django實現分頁功能
    在本節中,我們將介紹 Django 為我們提供的高級模塊,通過高級模塊的學習,你會感受到 Django 的易用性如此之強,但是同時它又很複雜,所以在學習
  • Django ORM源碼解析(一)
    import osimport sysif __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "OpsManage.settings") from django.core.management import execute_from_command_line
  • python程式設計師嘔心瀝血整理 Django 優秀資源大全
    django-compat, star:91 - 為官方支持的 Django 版本提供向前和向後兼容層。django-compat-lint, star:36 - 為你的代理檢查 Django 兼容性(2 年未更新)。儀錶盤用於創建信息儀錶盤以可視化數據的包。
  • Django Form表單API詳解
    8)在實際的開發工作中使用類的方式是非常方便,可以幫助開發者減少編寫重複的代碼的工作,而且代碼也顯得更為整潔,所以這種方式也是我們推薦使用的。field 參數為欄位的名稱。如果值為None,error 將作為 Form.non_field_errors() 的一個非欄位錯誤即沒有該欄位相關聯的錯誤類型。2) Form.has_error(field, code=None)判斷某個欄位是否具有指定 code 的錯誤。當 code 為 None 時,如果欄位有任何錯誤它都將返回 True。
  • Django性能指南:如何提升Django應用速度
    django_query_analyze是我編寫的裝飾器,用於計算資料庫查詢次數和運行該函數的時間,其具體實現參見附錄。既然如此,為何沒在開篇就介紹此技巧呢?因為索引遠比在模型欄位上設置db_index=True複雜。在常被訪問的列上建立索引可以提高與其相關的查詢速度。然而建立索引會付出額外的存儲空間代價,因此需要權衡成本與收益。通常,創建索引會減慢插入/更新的速度。
  • Django2.1文檔12-07:編寫你的第一個 Django 應用,第 5 部分
    測試使你的代碼更有吸引力你也許遇到過這種情況:你編寫了一個絕贊的軟體,但是其他開發者看都不看它一眼,因為它缺少測試。沒有測試的代碼不值得信任。Django 最初開發者之一的 Jacob Kaplan-Moss 說過:「項目規劃時沒有包含測試是不科學的。」其他的開發者希望在正式使用你的代碼前看到它通過了測試,這是你需要寫測試的另一個重要原因。
  • Django第二十八課
    在引用時做如下處理,然後在關聯外鍵User時,替換為settings.AUTH_USER_MODEL我們需要將所有migrations文件夾下的所以遷移文件(000x.py)全部刪除,並將文件中的db.sqlite3重命名為db1.sqlite3。接著我們重新生成遷移文件,並使用python manage.py createcachetable創建緩存表,最後使用python mange.py createsuperuser創建超級管理員。
  • Django 中 REST API 的設計
    在Django中,不需要自己去設計每一個API,因為djangorestframwork幫我們做了一些工作。其實設計Django REST API的框架不少,但是djangorestframwork風格更像Django,與django的集成度更高,更易上手。該框架分為model, serializer, views三層,支持權限許可等功能。
  • django模型 和 Django QuerySet API
    Django 模型(資料庫)資料庫相關的代碼一般寫在models.pydjango-admin.pystartproject learn_models # 新建一個項目cd learn_models # 進入到該項目的文件夾django-admin.py startapppeople # 新建一個 people 應用(app)
  • Django分頁完整示例
    它實際上是承擔將QuerySet拆分為Page對象的工作。在django中可以使用兩種方法進行分頁,第一種方法是使用基於函數的視圖,第二種方法是使用基於類的視圖。現在,首先,需要使用此命令創建一個新的django項目。