只有Python女程式設計師才知道的PonyORM

開發項目時,經常會用到ORM,而Python裡能用的ORM其實也沒幾個,以我目前的開發經驗來看,比較順手的ORM有Django內置的那個,或是寫Flask時,可以配合使用大佬封裝好的Flask-SQLAlchemy。而它們都與框架綁定,當你想在Web應用之外的項目裡使用ORM,體驗就沒這麼優雅了。當你要直接使用SQLAlchemy時,它才向你展現出它的利爪和尖牙——這玩意實在是太特麼難用了。

當然,人一切的痛苦,都是對自己無能的憤怒。SQLAlchemy有著極其強大且豐富的功能,同時也有著與之相匹配的較高的複雜度和上手難度(如果沒有Flask-SQLAlchemy這樣的東西,光是自己折騰session管理,就能耗費掉無數的青春),用不明白,只能說明自己菜。

面對著像大辭海一樣的SQLAlchemy文檔,我不禁陷入沉思—— 人活著到底是為了什麼 。ORM原本是為了降低心智負擔,提高開發效率和質量,而用這麼複雜的一個東西,我的開發效率已經不知道低到哪裡去了。更何況用Flask這樣輕量化的框架,配合這樣重量級的ORM,怎樣都覺得十分怪異。

直到我遇到了她。

只有Python女程序員才知道的PonyORM

不對,似乎放錯圖了。

只有Python女程序員才知道的PonyORM

PonyORM 是我最近開始嘗試在實際項目中使用的ORM,它優雅的使用方式,簡單的上手難度,以及不錯的開發效率,讓我眼前一亮,相當Pythonic!

其實以前曾經聽說過這玩意,但當時它是以AGPLv3授權,就沒去關注,但在2016年底的0.7版本開始,改為了Apache 2.0許可,這樣就友好了許多,值得一用了。

簡單描述一下它的特性:

  • 方便的查詢書寫方式(基於解析lambda和生成器語法樹的黑魔法)
  • 簡潔的Model定義方式(大部分基於語言內置類型進行定義)
  • 自動的數據庫事務管理
  • 支持MySQL, PostgreSQL, Oracle, SQLite數據庫
  • 在線的圖形化Model編輯器 Online Editor 。可以通過畫ER圖來定義你的模型數據表和它們之間的關係,自動生成class代碼,以及幾種數據庫的建表語句

例子:博客應用

那麼接下來以一個常見的博客應用為例,來看看如何使用這個PonyORM,這裡不涉及web,僅包含與orm相關的部分。PonyORM更復雜的使用方式以及介紹請參考它的 文檔

安裝

pip install ponyorm

如果你用MySQL,需要裝個相應的底層驅動 pymysql ,其他的數據庫類似,具體參考文檔 Connecting to the Database

導入

from pony.orm import *

數據庫連接

# 創建數據庫對象db = Database()
# 建立數據庫連接db.bind(provider='mysql', host='localhost', user='user', passwd='pass', db='mydb')

定義模型

class Post(db.Entity): post_pk = PrimaryKey(int, auto=True) title = Required(str) content = Optional(LongStr) published_at = Required(datetime, default=datetime.now) categories = Set("Category") comments = Set
("Comment")
class Category(db.Entity): name = Required(str) posts = Set("Post")
class Comment(db.Entity): post = Optional(Post) content = Required(str) published_at = Required(datetime, default=datetime.now)

對於關聯關係,它自動幫你生成外鍵,以及關聯表。如果你沒指定主鍵字段,它會生成一個默認的自增主鍵字段。

生成mapping並建表

db.generate_mapping(create_tables=True)

插入數據

with db_session:
Post(title="第一篇文章", content="Hello world") commit()

所有對數據庫的讀寫都要在db_sesion中進行,除了通過with當作context manager使用,也可以作為裝飾器,讓db_session對整個函數中有效(並且db_session允許嵌套)

更新了數據記得commit提交。

@db_sessiondef create_post(title, content): Post(title=title, content=content) commit()create_post(title="第2篇文章", content="Hello world too")create_post(title="第3篇文章", content="Hello world 3")

修改數據

with db_session: p = Post.get(post_pk=1) # 使用get來獲取一條數據,如果沒有查到會返回None p.content = "new content" commit()

關聯數據操作

給第一篇post添加一些評論吧。

with db_session: p = Post.get(post_pk=1) Comment(content="你瞅啥", post=p) Comment(content="瞅你咋地", post=p) commit() # 查看關聯的數據 print(p.comments)

之後就可以通過 p.comments 取到與之關聯的評論。

那麼再來試試多對多關係。

with db_session: c1 = Category(name="tech") c2 = Category(name="blog") commit() Post(title="第5篇文章", content="Hello world too", categories=[c1]) Post(title="第6篇文章", content="Hello world 3", categories=[c1, c2]) commit() # 查看關聯的數據 print(Category["tech"].posts) # 這個Category["tech"]等同於Category.get("tech") print(Post[6].categories)

刪除

調用Entity實例的 .delete() 方法可以刪掉這條數據。如果需要把相關聯的數據一併刪掉,需要在定義model字段的時候加上 cascade_delete=True 的參數。

with db_session: Category["tech"].delete() commit()

查詢

PonyORM的查詢方式比較魔性,和別的ORM有較大區別,這裡給個簡單的例子看看樣子。

用Entity對象上的select方法,傳入lambda表達式進行查詢,查了id大於2並且內容包含"world"的條目。

with db_session: query = Post.select(lambda p: p.post_pk > 2 and "world" in p.content) print(list(query)) # 將query對象轉為list,觸發真正的查詢獲取數據

使用另一種方式用select函數,傳入一個生成器表達式作為參數,查詢了以"咋地"結尾的Comment。

with db_session: query = select(p.content for p in Comment if p.content.endswith("咋地")) print(query[:]) # 另一種轉list的方式

使用SQL直接查詢。

with db_session: query = Category.select_by_sql("SELECT * FROM category LIMIT 1") print(query[:])

總結

PonyORM的魔法黑到飛起,寫起來還是比較爽的,但由於沒有太過深入使用過這個框架,這些黑魔法在複雜查詢時效果未知(實在不行你可以直接寫raw sql嘛,感覺也沒太大問題)。我目前在公司的項目中已經用它替換掉了SQLAlchemy,目前感覺良好,運行比較平穩,還沒踩到什麼坑。(FLAG)

那麼歡迎你也成為愛馬仕,一起踩坑~★


分享到:


相關文章: