國人開源的非同步 Python ORM:GINO

2022年10月28日07:26:33 熱門 1075

國人開源的非同步 Python ORM:GINO - 天天要聞

來源:GINO 官方文檔

本文約4810字,建議閱讀14分鐘

本文介紹GitHub 千星項目 GINO——可以既方便快捷、又簡單明了地訪問資料庫。

隨著 Tornado[1] 和 asyncio[2] 等框架的陸續湧現,Python 非同步編程這個話題也在逐漸升溫。在這個燒腦的非同步世界裡,有沒有辦法可以既方便快捷、又簡單明了地訪問資料庫呢?GitHub 千星項目 GINO 了解一下!

國人開源的非同步 Python ORM:GINO - 天天要聞

1. GINO 是誰

GINO 是一個「輕量級」非同步 ORM 框架,它的全稱是 GINO Is Not ORM,借鑒了 GNU is Not Unix 的遞歸定義[3] 手法。所以,GINO 一定要全!部!大!寫!如果像這樣「Gino」就變成了人名,你肯定要問一句「這是誰」。

ORM,即關係對象映射(Object-Relational Mapping[4]),是一類開發人員喜聞樂見的效率工具,它們"極大地"提升了寫代碼的幸福指數。GINO 是用來訪問資料庫的,也提供了對象映射的工具,那為什麼非說 GINO 不是 ORM 呢?

因為物極必反,ORM 在帶來生活便利的同時,也是 bug 生長的溫床 —— 傳統 ORM 往往會選擇犧牲明確性(explicitness)來換取便捷性(convenience),再加上 Python 得天獨厚的靈活性(flexibility),創造出了一種爆炸式的化學反應。一旦代碼初具規模,項目或多或少都會遇到 ORM 反噬的情景:性能莫名其妙的差、出問題找不到原因、為了雞毛蒜皮的小事大動干戈。隨便一句 current_user.name 都有可能觸發一大堆意想不到的資料庫調用,這代碼你讓我怎麼調試?

傳統 ORM 的學習曲線前平後陡,能在快速原型開發中大展身手,但應用到大型項目中卻十分考驗開發人員的平均水平。

所有這些問題如果再放進非同步編程的環境里,那就是 O(n2) 的複雜度了 —— 哦不,是 O(2n)。這對於一款優秀的非同步 ORM 框架來說是不可接受的,所以 GINO 是 ORM 但不是一個傳統的 ORM,正猶如 GNU 不是一個傳統的 Unix 一樣,形似而神不似。

所以在 2017 年創作之初,我就給 GINO 定下了兩個業績目標:1) 方便快捷,2) 簡單明了。三年後的今天,我索性在 1.0 穩定版發布的前夕做個年終總結。

2. 先說"方便快捷"

"方便快捷"主要說的是開發效率。

重視開發效率的概念對於寫 Python 的同學來說可能並不陌生,某些場景下,開發人員的時間確實比機器的時間值錢。所以,傳統 ORM 里的對象映射不能丟。

from gino import Ginodb = Gino()class User(db.Model):    __tablename__ = "users"    id = db.Column(db.Integer, primary_key=True)    name = db.Column(db.String)

這麼定義表結構甚至讓人有點小興奮。咦,為什麼這麼眼熟?

沒有錯,這就是 SQLAlchemy ORM[5] 的定義風格。GINO 並不是從頭造輪子,而是在 SQLAlchemy core[6](SQLAlchemy 中負責構建 SQL 的底層核心)的基礎上開發的。這麼做除了能保持熟悉的味道(以節省學習和遷移成本),更重要的是帶來了整個 SQLAlchemy 的生態環境:開箱即用的資料庫變更管理工具 Alembic[7]、各種 SQLAlchemy 的增強插件[8]、專業領域的 PostGIS/geoalchemy[9] 等,GINO 全都兼容。

是不是十分方便、十分快捷?不止這樣。

GINO 一站式地解決了常用 CRUD 快捷方式[10]、上下文管理(aiocontextvars[11][12])、資料庫事務封裝和嵌套[13]、連接池管理和懶載入[14] 等多項便捷功能,無額外依賴關係,即裝即用。

daisy = await User.create(name="daisy")await daisy.update(name="Daisy").apply()

GINO 還提供了[15] 各大流行非同步 Web 框架的定製版插件,能叫上名字的像 Tornado[1]、aiohttp[16]、Sanic[17]、FastAPI[18]/Starlette[19]、Quart[20] 什麼的都有,從簡單示範到生產環境的各種例子品種齊全,媽媽再也不用擔心我不會集成 Web 框架了。

為了讓不同應用場景下的用戶體驗到最大的善意,GINO 目前支持三種不同程度的用法,成功實現了對同期競品 asyncpgsa[21] 的降維打擊:

  1. 最少侵入型[22]:SQLAlchemy core 原教旨主義者,只有非同步執行時才用到 GINO。
  2. 終身不婚型[23]:天生厭惡"對象",只願定義"表",空手接 SQL。
  3. 火力全開型[24]:最大程度的便利,非典型非同步 ORM。

最後,雖然是 Python(絕不是黑哈),但 GINO 在執行效率上也沒落下。基於 MagicStack 出品必屬精品的、一秒可讀百萬行的 asyncpg[25],以及 uvloop[26](可選)的強力加持,GINO 跑起來也是可以飛快的,被廣泛應用於諸如實時匯率、聊天機器人、在線遊戲等高並發領域,深受俄羅斯和烏克蘭人民的愛戴。

3. 再說"簡單明了"

Explicit is better than implicit.

Simple is better than complex.

-- The Zen of Python, PEP 20[27]

Python 之禪完美表達了 GINO 的立場 —— 明確性(explicitness)對於上了規模的非同步工程項目來說尤為重要,因此 GINO 的很多設計都受到了明確性的影響。

比如說,GINO 的 Model 是完全無狀態的普通 Python 對象(POPO[28])—— 例如前面的 User 類,它的實例

daisy 就是內存裡面的常規對象,你可以用 daisy.name 訪問屬性,也可以用 daisy.name = "DAISY" 來修改屬性,或者用 u = User() 來創建新的實例,這些操作都不會訪問資料庫,絕對綠色環保無毒副作用。

等到需要操作資料庫的時候,你一定會有感知的。比如執行 INSERT 要用 u = await User.create(),而 UPDATE 則是 await u.update(name="Daisy").apply()。

其中, u.update(name="Daisy") 與 u.name = "Daisy" 類似,都是只在內存里修改對象的屬性,不同的是 u.update() 還會返回一個包含本次變更的中間結果,對其執行 await xxx.apply() 則會將這些變更應用到資料庫里。

這裡的 await 就是明確性的關鍵,意味著我們要跳出去執行資料庫操作了。換句話說,沒有 await 就沒有資料庫操作。

另一方面,對於如何將資料庫查詢結果組裝成內存對象及其屬性,GINO 也有一套精妙的顯式機制 —— 可定製化的載入器 loaders[29]。對於簡單直觀的一對一載入,GINO 自然是伺候到家的,比如用 u = await User.get(1) 可以直接獲取到 ID 為 1 的用戶對象。但是對於更複雜的查詢,GINO 不會去無端猜測主人的意圖,而全權交給用戶來明確地定義。載入器的用法也是很簡單的,比如一個用戶可能寫了很多本書:

class Book(db.Model):    __tablename__ = "books"    id = db.Column(db.Integer, primary_key=True)    title = db.Column(db.String)    author_id = db.ForeignKey("users.id")

然後這樣來載入這種多對一關係,以同時獲取所有的書和他們的作者:

query = Book.outerjoin(User).select()loader = Book.load(author=User)async for book in query.gino.load(loader).iterate():    print(book.title, "written by", book.author.name)

很簡單的一個外連接查詢 Book.outerjoin(User),配合一個直觀的載入器 Book.load(author=User),就實現了:

  1. 執行 SELECT * FROM books LEFT JOIN users ON ...;
  2. 將資料庫返回結果的每一行中,屬於 books 的欄位載入成一個 Book 實例;
  3. 然後將該行中剩下的屬於 users 的欄位載入成一個 User 實例;
  4. 最後將 User 實例設置到 Book 實例的 author 屬性上。

既簡單又明了有沒有!你甚至可以手寫任何 SQL,然後定製載入器自動載入成期望的對象關係,精準控制載入行為,指哪兒打哪兒。GINO 還有很多類似的特性,在這裡就不一一列舉了。

4. 優勢與不足

隨著這幾年 GINO 不斷演進成熟,Python 開源社區里也相繼出現了像 Tortoise ORM[30]、ORM[31](是的,這個項目就叫 ORM......我真 ORZ。出品方是 Encode,Starlette 就是他們的作品)等優秀的非同步 ORM 框架。它們關注的重點與 GINO 稍有不同,但都是同行就不多評價了。

GINO 的最大優勢還是在於充分平衡了開發效率和明確性之間的辯證矛盾關係,用 GINO 開發應用程序的時候不用擔心會被意料之外的行為所驚嚇到,同時也不需要為這種明確性付出過大的工程代價,上手後依然可以快速、快樂地編程。同時,大量的成功案例也證明了 GINO 已經初步具備發布 1.0 穩定版的各種條件,可以謹慎地用於生產環境了。

以下是近來統計到的關於 GINO 的應用案例:

  • 筆者工作上開發的一個服務:https://github.com/uc-cdis/metadata-service
  • 還是筆者自己寫的一個工具:https://github.com/fantix/aintq
  • 一個匯率 API 服務:https://exchangeratesapi.io/

國人開源的非同步 Python ORM:GINO - 天天要聞

  • 各種 Telegram、Discord 的 Bot。
  • ArchLinux 用戶包:https://aur.archlinux.org/packages/python-gino/

國人開源的非同步 Python ORM:GINO - 天天要聞

  • https://github.com/bryanforbes/gino-stubs/
  • 俄語教程:https://www.youtube.com/watch?v=WW4rOnfhiQY
  • 高性能模板項目:https://github.com/leosussan/fastapi-gino-arq-uvicorn
  • 還有幾個商用的,但沒徵得同意就不貼出來了。

另外,GINO 還貼心地提供了中文文檔[32],從上手教程到原理說明應有盡有(雖然文檔還在努力編寫中!):

國人開源的非同步 Python ORM:GINO - 天天要聞

GINO 目前的不足之處還有一些,比如沒有照顧到 Python 3 的類型提示,因此還不能完全發揮 IDE 的潛能(上面那個 gino-stubs 就是有人受不了了自己寫了一個類型註解)。MySQL 目前也是不支持的,但 GINO 從比較早就解耦了不同 SQL 方言和驅動的集成,所以這些功能會陸續在 1.1 和 1.2 版本中跟上。

5. 建設社會主義

GINO 是一個開源項目,所以歡迎大家一起來建設!

目前急需幫助的有:

  1. 各個 Web 框架插件的維護工作需要多人認領;
  2. 更多的例子和文檔,以及中文、俄文的翻譯;
  3. MySQL 的支持。

以及下面這些一直需要的幫助:

  1. 用 GINO,找 bug,提建議;
  2. 修 bug,做功能,提 PR;
  3. 維護社區,回答問題,參與討論;
  4. 最後也是最重要的:去 GitHub 上給 GINO 加一顆星星!

6. 關於作者

I'm a software architect, fan of coding for over 20 years, and now focusing on software engineering, high concurrency and development performance. Python is my chief language, and I worked on Linux for 10 years, managing development teams up to 20 people for 8 years. Big fan of open source, created project GINO with 1000+ GitHub stars.

OPEN SOURCE

Python / 2018-2019

I started to contribute to Python programming language with MagicStack fellows, focusing on the asyncio library.

GINO / 2017-2019

GINO is an ORM library for Python asyncio. It is an integration of SQLAlchemy core and asyncpg.

aioh2 / 2016

This is an integration of an HTTP/2 protocol library and Python 3 asyncio.

tulipcore / 2015

I've implemented the most part of the event loop core of Gevent with pure Python 3 asyncio (tulip) code.

zmq.rs / 2014

It was my attempt to implement the ZeroMQ stack in pure Rust.

ArchLinux / 2013 - 2016

I maintained the packages of a dozen of build toolchains and base libraries of ArchLinux for x32-ABI, e.g. GCC, glibc, openssl, curl, util-linux, etc.

Gevent / 2011 - 2013

I contributed the initial port of Gevent to Python 3, which was later merged into Gevent 1.1 by its new maintainer. I've also ported Greenlet to x32-ABI.

Translations / 2008 - 2015

I was involved/started several translation projects, e.g. Ubuntu, libexif, Twisted, Python-beginners, ZeroMQ, etc.

87 年生人,6 歲開始接觸編程,二十五六年的編程史和十三四年的工作經驗教會了我許多軟體開發的奧義。小時候寫 GBasic、QBasic 和 Visual Basic;大學裡開始寫 Java 並接觸到了 FreeBSD 和 Ubuntu 等開源項目且一發不可收拾;工作頭五年轉向了 Python,通過 Twisted 和 Eventlet 等項目了解了非同步編程,期間貢獻了 Gevent 的 Python 3 遷移;也曾在創業的潮流中留下身影,親身經歷並見證了軟體技術隨著手游、新媒體、礦圈、互金、電商、社交、文娛、汽車等行業的起起伏伏;目前在芝大繼續做生物大數據相關的開源項目。業餘時間除了開發維護 GINO 項目外,還會偶爾修一修 uvloop、asyncpg 和 CPython 的 bug :P

7.參考文獻

[1] Facebook. Tornado.

URL: https://zh.wikipedia.org/wiki/Tornado.

[2] Python Software Foundation. asyncio — 非同步 I/O.

URL: https://docs.python.org/zh-cn/3/library/asyncio.html.

[3] 維基百科. GNU.

URL: https://zh.wikipedia.org/wiki/GNU.

[4] 維基百科. 對象關係映射.

URL: https://zh.wikipedia.org/wiki/對象關係映射

[5] 維基百科. SQLAlchemy.

URL: https://zh.wikipedia.org/wiki/SQLAlchemy.

[6] Michael Bayer. SQLAlchemy Core - SQLAlchemy . Documentation.

URL: https://docs.sqlalchemy.org/en/13/core/index.html.

[7] Michael Bayer. Welcome to Alembic』s documentation!

URL: https://alembic.sqlalchemy.org/en/latest/.

[8] Hong Minhee. A curated list of awesome tools for SQLAlchemy.

URL: https://github.com/dahlia/awesome-sqlalchemy.

[9] Does it have postgis support? Does it have postgis support?

URL: https://github.com/python-gino/gino/issues/627.

[10] Fantix King. GINO 基礎教程 - 增刪改查.

URL: https://python-gino.org/docs/zh/master/tutorials/tutorial.html#crud-operations.

[11] Python Software Foundation. contextvars —Context Variables.

URL: https://docs.python.org/3/library/contextvars.html.

[12] Fantix King. gino/aiocontextvars.py.

URL: https://github.com/python-gino/gino/blob/f2c273a43a3ed893a767d4239046f2befabf510d/src/gino/aiocontextvars.py.

[13] Fantix King. 資料庫事務.

URL: https://python-gino.org/docs/zh/master/how-to/transaction.html.

[14] Fantix King. 引擎與連接.

URL: https://python-gino.org/docs/zh/master/explanation/engine.html.

[15] GINO Community. GINO Community.

URL: https://github.com/python-gino/.

[16] aiohttp maintainers. Welcome to AIOHTTP.

URL: https://docs.aiohttp.org/en/stable/.

[17] Sanic Community. Sanic Framework.

URL: https://sanicframework.org/.

[18] Sebastián Ramírez. FastAPI.

URL: https://fastapi.tiangolo.com/.

[19] Encode OSS. Starlette.

URL: https://www.starlette.io/.

[20] Philip Jones. Quart Documentation.

URL: https://pgjones.gitlab.io/quart/.

[21] Canopy. asyncpgsa - A wrapper around asyncpg for use with sqlalchemy.

URL: https://github.com/CanopyTax/asyncpgsa.

[22] Fantix King. 表結構定義 - GINO 引擎.

URL: https://pythongino.org/docs/zh/master/how-to/schema.html#gino-engine.

[23] Fantix King. 表結構定義 - GINO core.

URL: https://python-gino.org/docs/zh/master/how-to/schema.html#gino-core.

[24] Fantix King. 表結構定義 - GINO ORM.

URL: https://python-gino.org/docs/zh/master/how-to/schema.html#gino-orm.

[25] MagicStack Inc. asyncpg - A fast PostgreSQL Database Client Library for Python/asyncio.

URL: https://github.com/MagicStack/asyncpg.

[26] MagicStack Inc. uvloop - Ultra fast asyncio event loop.

URL: https://github.com/MagicStack/uvloop.

[27] Tim Peters. PEP ‒ The Zen of Python.

URL: https://www.python.org/dev/peps/pep-0020/.

[28] Wikipedia. Plain old Java object.

URL: https://en.wikipedia.org/wiki/Plain_old_Java_object.

[29] Fantix King. 載入器與關係.

URL: https://python-gino.org/docs/zh/master/how-to/loaders.html.

[30] Andrey Bondar. Tortoise ORM.

URL: https://tortoise.github.io/.

[31] Encode OSS. ORM - An async ORM.

URL: https://github.com/encode/orm.

[32] Yury Selivanov. PEP ‒ Coroutines with async and await syntax.

URL: https://www.python.org/dev/peps/pep-0492/. (Apr , ).

想要獲得更多數據科學領域相關動態,誠邀關注清華-青島數據科學研究院官方微信公眾平台「 數據派THU 」。

熱門分類資訊推薦

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO - 天天要聞

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO

曾小賢的上司Lisa榕,現實中不僅才貌雙全,還嫁給了CEO雖然說《愛情公寓》這部劇在劇情上充滿了爭議,但是一定程度上,這部劇也是很多人的回憶,是伴隨了一代人的青春回憶,而且劇中的很多角色都成為了經典,他們的口頭禪也一直被拿來玩兒梗。
Lisa榕做主持多年沒紅,被陳赫拉進愛情公寓爆紅,如今怎樣了 - 天天要聞

Lisa榕做主持多年沒紅,被陳赫拉進愛情公寓爆紅,如今怎樣了

談到《愛情公寓》這部火爆一時的歡樂喜劇,大家肯定都不陌生。不知道大家是否還記得《愛情公寓》中那個把曾小賢治得服服帖帖的女上司Lisa榕,現實中的她名叫榕榕,和劇中的形象也判若兩人。1981年出生在遼寧瀋陽的榕榕,畢業於上海戲劇學院,後來成為了上海東方傳媒集團有限公司的一名主持人。