愛上 SQLAlchemy 的 10 個理由

Namco發表於2015-10-22

最近,我見到了很多針對 ORM 的抨擊,但是我覺得有些批評是莫須有的。我本人就是 SQLAlchemy 的忠實擁躉。在我的專案裡很多地方都用到了 SQLAlchemy,我也為 SQLAlchemy 專案貢獻了一些程式碼。這篇文章裡我會闡述你應當愛上 SQLAlchemy 的10個理由。說實話,除了 SQLAlchemy 以外還有很多優秀的 ORM,我所闡述的大部分理由同樣適用於它們但是 SQLAlchemy 是我的最愛。

1、用應用程式程式碼定義資料庫模式

SQLAlchemy 允許你使用 Python 程式碼來定義資料庫模式(schema)。下面是一個電子商務網站的例子,一個訂單就代表一條記錄。

定義資料庫模式的 Python 程式碼在 SQLAlchemy 中叫做模型(model)。因為這些模型都是用 Python 的類實現的,所以你可以新增自己的類方法。這樣可以把相關功能放在一起方便維護。

上面這個例子說明了在 SQLAlchemy 中資料庫的模式也可以通過版本控制來維護。所以使用SQLAlchemy 的同時,你也可以享受到版本控制的諸多便利:比如版本追蹤、標籤、追溯(blame)等等。

2、自動同步模型到資料庫模式

Alembic 是 SQLAlchemy 的一個資料庫管理外掛。當你修改模型的時候,Alembic 可以自動更新資料庫模式。使用 Alembic 來做一些新增表或者列的小改動是非常便捷快速的。

儘管在開發環境中自動同步非常方便,但是大多數人還是想在生產環境中採用更穩妥一些的方式。這一點 Alembic 也想到了,它可以自動生成修改的指令碼,資料庫管理員可以在檢視指令碼之後再應用到生產環境的資料庫上。

3、Pythonic 的程式碼風格讓你的程式碼更易讀

SQLAlchemy 使用更 Pythonic 的方式表示資料庫關係,這對於閱讀和編寫程式碼來說是非常方便的。我們來看下面這個例子,這個例子可以列印出在一個訂單中的所有產品

程式碼非常的簡單易讀,但是打通了兩個資料庫,執行了 JOIN 連線查詢。order.items 是一對多的關係,SQLAlchemy 會自動載入 OrderItem 中和這個訂單相關的物件。item.product 則儲存的是多對一的關係,SQLAlchemy 會自動載入對應的產品。

SQLAlchemy 也可以運用類。如果應用修改了一個有對映的欄位,物件會自動請求寫入資料庫。這個功能使編寫應用邏輯的時候沒有了後顧之憂。

4、用 Python 構建查詢語句

類似用主鍵獲取物件這種簡單查詢只需要很少的程式碼:

使用 Python 的查詢語法,我們可以實現更復雜的查詢。比如下面的例子,我要查詢兩天以前的有效訂單:

SQLAlchemy 的語法可以讓你把 SQL 語句和 Python 的變數結合起來,並且可以杜絕 SQL 注入攻擊。SQLAlchemy 會在內部過載各種比較運算子,然後將它們轉換成 SQL 語句。

當你執行一條非常複雜的查詢時,使用 SQLAlchemy 語法來定義查詢也是可以的。但是我認為 SQLAlchemy 可以勝任的查詢複雜程度是有限的,某些時候直接寫 SQL 語句可能更易些。在這種情況下,你可以定義資料庫檢視來完成複雜查詢,SQLAlchemy 可以把檢視對映到 Python 物件。

5、與 Web 框架的無縫整合

有些架預設支援SQLALchemy ,比如說 Pyramid。對於其他 web 框架,你需要安裝一個整合庫來支援 SQLAlchemy,比如說用於 Flask 的 Flask-SQLAlchemy 或者用於 Django 的 aldjemy。

SQLAlchemy 會維持一個連線池,為每一個 web 請求提供一個可用的資料庫連線。那些支援庫可以處理常見異常,提高應用的健壯性,讓應用在某些異常情況下不至於崩潰,比如執行時重啟資料庫這樣的操作。

每一個請求都會用事務包裹起來,如果請求成功就提交事務,否則就回滾。這種設計使得外部方法能夠正確地與資料庫互動,而不需要關心具體的資料庫處理程式碼。

6、預先載入提高效能

大部分 ORM 都使用的是延遲載入策略。在第一次呼叫關係時,一次 SQL 查詢會執行,載入資料。像上面的例子,order.items 的呼叫實際上執行了一次 SQL 查詢,之後的每次使用 item.product 都會發起另外的查詢。因為 item.product 是在一個迴圈中呼叫的,所以會生成大量的 SQL 查詢,導致效能降低。這種情況叫做“n+1 選擇問題”。

SQLAlchemy 針對上述問題有一個解決方案:預先載入(eager loading)。當我們第一次載入 Order 這個物件時,我們可以通知 SQLAlchemy 我們會使用那些關係,然後 SQLAlchemy 就能在一次查詢中載入所有資料,語法如下所示:

7、透明的多型支援

像 Python 這樣的面嚮物件語言是鼓勵使用多型的。如果有一個 Person 的基類,我們就可以基於 Person 來建立子類,比如 增加了新欄位的Employee 或者 Customer 類。但是傳統的 SQL 資料庫不支援多型,所以 ORM 想要支援多型也是有心無力。

但是 SQLAlchemy 在 SQL 中完美地模擬了多型。我們在 Python 程式碼中可以非常自然地使用多型,資料庫中的資料也可以通過 SQL 便捷地獲取。在應用程式碼中我們可以輕鬆地使用多型類,而不需要關心它們究竟是怎麼儲存的。

8、相容已有資料庫

一些 ORM 要求你的資料庫結構滿足既定條件,強制每張表都有單一主鍵列,甚至主鍵名稱必須是 “id”。如果你從頭建立一個資料庫,這些限制並不是問題。但是如果你想用以前的資料庫,這些限制條件會阻止你訪問那些不滿足條件的表。

SQLAlchemy 不會假定你的資料庫結構,所以可以完美支援以前的資料庫。還有一個叫做 sqlacodegen 的工具可以根據已有的資料庫生成 SQLAlchemy 模型。SQLAlchemy 使得你可以通過簡單的 Python 指令碼和以前的資料庫進行互動。

9、提供許多鉤子函式讓你自定義庫

SQLAlchemy 擁有清晰的分層架構。幾乎任何 SQLAlchemy 庫都可以被重寫以滿足特定需求。

當在有多個使用者的雲應用上工作的時候,我發現了一個非常有用的功能。比如說應用中大多數查詢都包含了過濾條件,只返回當前使用者的結果。就像下面這個例子:

但是我發現,如果一不小心忘記加過濾條件的話,可能讓某個使用者可以看到其他經銷商的資訊,而這是不允許的。謹慎起見,我們可以建立一個自定義的 SQLAlchemy 的 session 工廠函式,可以在會話中自動給所有查詢應用過濾條件。雖然只是這麼一點小小的控制程式碼,卻可以讓你的應用安全係數更高。

10、完善的文件

有些開源專案的文件的確是漏洞百出,但是 SQLAlchemy 不是這樣。SQLAlchemy 的文件非常詳盡,並且還有從簡單例子到高階特性的學習指南,以及全面的 API 參考文件。這對於開發人員學習和使用 SQLAlchemy 是非常有幫助的。

相關文章