靈活使用 SQLAlchemy 中的 ORM 查詢

2020-09-22 小小後端

之前做查詢一直覺得直接拼 SQL 比較方便,用了 SQLAlchemy 的 ORM 查詢之後,發現也還可以,還提高了可讀性。

這篇文章主要說說 SQLAlchemy 常用的 ORM 查詢方式,偏實踐。看了之後,對付開發中的查詢需求,我覺得可以滿足不少。

為方便說明,假設有如下數據

圖書表 books

+----+--------+--------------------------+-------+| id | cat_id | name | price |+----+--------+--------------------------+-------+| 1 | 1 | 生死疲勞 | 40.40 || 2 | 1 | 皮囊 | 31.80 || 3 | 2 | 半小時漫畫中國史 | 33.60 || 4 | 2 | 耶路撒冷三千年 | 55.60 || 5 | 2 | 國家寶藏 | 52.80 || 6 | 3 | 時間簡史 | 31.10 || 7 | 3 | 宇宙簡史 | 22.10 || 8 | 3 | 自然史 | 26.10 || 9 | 3 | 人類簡史 | 40.80 || 10 | 3 | 萬物簡史 | 33.20 |+----+--------+--------------------------+-------+

分類表 categories

+----+--------------+| id | name |+----+--------------+| 1 | 文學 || 2 | 人文社科 || 3 | 科技 |+----+--------------+

ORM 對象定義如下


注意:本文 Python 代碼在以下環境測試通過

  • Python 3.6.0
  • PyMySQL 0.8.1
  • SQLAlchemy 1.2.8

39;mysql+pymysql://username:password&39;@127.0.0.1:3306/db_name?charset=utf8&39;books&39;name&39;price&39;categories&39;name&39;id&39;cat_id&39;name&39;生死疲勞&39;price&39;40.40&39;id&39;cat_id&39;name&39;生死疲勞&39;price&39;40.40&39;id&39;cat_id&39;name&39;皮囊&39;price&39;31.80& 找不到記錄會拋如下錯誤 找到多條記錄會拋如下錯誤 正常,得到如下結果39;id&39;cat_id&39;name&39;萬物簡史& &39;: Decimal(&39;)}book = session \ .query(Book).filter(Book.id == 10) \ .one()print(book and book.to_dict())

  • count() 返回記錄條數

count = session \ .query(Book) \ .filter(Book.cat_id == 3) \ .count()print(count)

結果

5

  • limit() 限制返回的記錄條數

books = session \ .query(Book) \ .filter(Book.cat_id == 3) \ .limit(3) \ .all()print([v.to_dict() for v in books])

結果

[{&39;: 6, &39;: 3, &39;: &39;, &39;: Decimal(&39;)}, {&39;: 7, &39;: 3, &39;: &39;, &39;: Decimal(&39;)}, {&39;: 8, &39;: 3, &39;: &39;, &39;: Decimal(&39;)}]

  • distinct() 與 SQL 的 distinct 語句行為一致

books = session \ .query(Book.cat_id) \ .distinct(Book.cat_id) \ .all()print([dict(zip(v.keys(), v)) for v in books])

結果

[{&39;: 1}, {&39;: 2}, {&39;: 3}]

  • order_by() 將記錄按照某個欄位進行排序

如果要升序排列,去掉 .desc() 即可books = session \ .query(Book.id, Book.name) \ .filter(Book.id > 8) \ .order_by(Book.id.desc()) \ .all()print([dict(zip(v.keys(), v)) for v in books])

結果

[{&39;: 10, &39;: &39;}, {&39;: 9, &39;: &39;}]

  • scalar() 返回調用 one() 後得到的結果的第一列值

book_name = session \ .query(Book.name) \ .filter(Book.id == 10)\ .scalar()print(book_name)

結果

萬物簡史

  • exist() 查看記錄是否存在

39;id&39;cat_id&39;name&39;生死疲勞&39;price&39;40.40&39;id&39;cat_id&39;name&39;皮囊&39;price&39;31.80&39;id&39;cat_id&39;name&39;耶路撒冷三千年&39;price&39;55.60&39;id&39;cat_id&39;name&39;國家寶藏&39;price&39;52.80&39;id&39;cat_id&39;name&39;人類簡史&39;price&39;40.80&39;id&39;cat_id&39;name&39;耶路撒冷三千年&39;price&39;55.60&39;id&39;cat_id&39;name&39;宇宙簡史&39;price&39;22.10& 請求參數,這裡只是佔位,實際由用戶提交的請求決定params = {&39;: 1}conditions = []if params.get(&39;, 0): conditions.append(Book.cat_id == params[&39;])if params.get(&39;, 0): conditions.append(Book.price == params[&39;])if params.get(&39;, 0): conditions.append(Book.price >= params[&39;])if params.get(&39;, 0): conditions.append(Book.price <= params[&39;])books = session.query(Book).filter(*conditions).all()print([v.to_dict() for v in books])

結果

[{&39;: 1, &39;: 1, &39;: &39;, &39;: Decimal(&39;)}, {&39;: 2, &39;: 1, &39;: &39;, &39;: Decimal(&39;)}]

OR 查詢類似,將列表解包傳給 or_() 即可。

如果需求更複雜,AND 和 OR 都可能出現,這個時候根據情況多建幾個列表實現。這裡只向大家說明大致的思路,就不舉具體的例子了。

當然,如果都是等值查詢的話,比如只有這兩種情況

  • 如果接收到非 0 的 cat_id,需要限制 cat_id 等於 0
  • 如果接收到非 0 的 price,需要限制 price 等於傳入的 price

可以使用字典的解包給 filter_by() 傳參

39;price&39;cat_id&39;cat_id&39;cat_id&39;price&39;price&39;price&39;id&39;cat_id&39;name&39;時間簡史&39;price&39;31.10& 查詢 ID 在 1、3、5 中的記錄books = session.query(Book) \ .filter(Book.id.in_([1, 3, 5])) \ .all()

  • INSTR()

39;時間簡史& 查詢名稱包含「時間簡史」的圖書 FIND_IN_SET() 一般用於逗號分隔的 ID 串查找39;時間簡史& 查詢名稱以「簡史」結尾的圖書books = session.query(Book) \ .filter(Book.name.like(&39;)) \ .all()

  • NOT

上面的 IN、INSTR、FIN_IN_SET、LIKE 都可以使用 ~ 符號取反。比如

39;簡史&39;id&39;name&39;時間簡史&39;id&39;name&39;宇宙簡史&39;id&39;name&39;人類簡史&39;id&39;name&39;萬物簡史& 如果 ORM 對象中定義有外鍵關係 否則,必須要 books = session \ .query(Book.id, Book.name.label(&39;), Category.name.label(&39;)) \ .join(Category, Book.cat_id == Category.id) \ .filter(Category.name == &39;, Book.price > 40) \ .all()print([dict(zip(v.keys(), v)) for v in books])

結果

[{&39;: 9, &39;: &39;, &39;: &39;}]

統計各個分類的圖書的數量

from sqlalchemy import funcbooks = session \ .query(Category.name.label(&39;), func.count(Book.id).label(&39;)) \ .join(Book, Category.id == Book.cat_id) \ .group_by(Category.id) \ .all()print([dict(zip(v.keys(), v)) for v in books])

結果

[{&39;: &39;, &39;: 2}, {&39;: &39;, &39;: 3}, {&39;: &39;, &39;: 5}]

9.2 外連接

為方便說明,我們僅在這一小節中向 books 表中加入如下數據

+----+--------+-----------------+-------+| id | cat_id | name | price |+----+--------+-----------------+-------+| 11 | 5 | 人性的弱點 | 54.40 |+----+--------+-----------------+-------+

查看 ID 大於等於 9 的圖書的分類信息

如果 ORM 對象中定義有外鍵關係 否則,必須要books = session \ .query(Book.id.label(&39;), Book.name.label(&39;), Category.id.label(&39;), Category.name.label(&39;)) \ .outerjoin(Category, Book.cat_id == Category.id) \ .filter(Book.id >= 9) \ .all()print([dict(zip(v.keys(), v)) for v in books])

結果

[{&39;: 9, &39;: &39;, &39;: 3, &39;: &39;}, {&39;: 10, &39;: &39;, &39;: 3, &39;: &39;}, {&39;: 11, &39;: &39;, &39;: None, &39;: None}]

注意最後一條記錄。

10 列印 SQL

當碰到複雜的查詢,比如有 AND、有 OR、還有連接查詢時,有時可能得不到預期的結果,這時我們可以打出最終的 SQL 幫助我們來查找錯誤。

以上一節的外連接為例說下怎麼列印最終 SQL

q = session \ .query(Book.id.label(&39;), Book.name.label(&39;), Category.id.label(&39;), Category.name.label(&39;)) \ .outerjoin(Category, Book.cat_id == Category.id) \ .filter(Book.id >= 9)raw_sql = q.statement \ .compile(compile_kwargs={&34;: True})print(raw_sql)

其中,q 為 sqlalchemy.orm.query.Query 類的對象。

結果

SELECT books.id AS book_id, books.name AS book_name, categories.id AS cat_id, categories.name AS cat_name FROM books LEFT OUTER JOIN categories ON books.cat_id = categories.id WHERE books.id >= 9

至此,SQLAlchemy ORM 常用的一些查詢方法和技巧已介紹完畢,希望能幫助到有需要的朋友。

原文連結:http://www.kevinbai.com/articles/30.html

關注公眾號「小小後端」獲取最新文章推送!

相關焦點

  • 學好ORM框架SQLAlchemy面試必問
    首先我們先來了解一下SQLAlchemySQLAlchemy是Python程式語言下的一款ORM框架,該框架建立在資料庫API之上,使用關係對象映射進行資料庫操作,簡言之便是:將對象轉換成SQL,然後使用數據API執行SQL並獲取執行結果。
  • SQLAlchemy 1.3.8 發布,Python ORM 框架
    orm[orm] [bug] 修復了由於內部上下文字典中的映射器/關係狀態導致 Load 對象不可拾取的錯誤[orm] [usecase> 添加了對使用 Python pep-435 枚舉對象的 Enum 數據類型的使用的支持,作為用作 ORM 映射的主鍵列的值engine[engine] [feature] 添加了新參數 create_engine.hide_parameters,當設置為 True 時,將導致不再記錄 SQL 參數,也不會在 StatementError 對象的字符串表示形式中呈現
  • Python資料庫ORM工具sqlalchemy的學習筆記
    ,並且支持強大的ORM的功能,下面是基本使用 from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker  # 資料庫連接字符串 DB_CONNECT_STRING = 'sqlite:////Users/zhengxiankai/Desktop/Document/db.sqlite'  #
  • Python通過SQLAlchemy-ORM訪問操作MySQL資料庫
    中已經對 &34; 進行了介紹。中已經對 &34; 進行了介紹。中已經對 &34; 進行了介紹。這意味著添加了一個額外的抽象層,這樣我們在使用Python語言對資料庫進行訪問操作時,就可以直接使用Python語言的對象模型(SQLAlchemy應用程式接口)來跟資料庫打交道,而不用直接使用SQL語句了。
  • SQLAlchemy 1.2.7 發布,Python 的 ORM 框架
    SQLAlchemy 1.2.7 已發布,引入了一系列針對 Core 和 ORM 的修復:[orm] [bug] Fixed regression
  • SQLAlchemy基礎入門
    它有幾個不同的功能領域,可以單獨使用或組合在一起。如何查看安裝的版本使用以下代碼可以查看SQLAlchemy的版本號,建議使用最新的正式版本。>>> importsqlalchemy>>> sqlalchemy.
  • SQLAlchemy 1.3.12 發布,Python ORM 框架
    更新內容如下:orm 修復了涉及 lazy="raise" 策略的問題,在該策略中,對象的 ORM 刪除將引發配置了 lazy="raise" 的簡單 「use-get」 樣式多對一關係。修復了 1.3.0 中引入的回歸,它與 #4351 中的關聯代理重構有關,該回歸阻止了 composite() 屬性在引用它們的關聯代理方面發揮作用。 現在,在 relationship() 上設置與持久性相關的標誌,同時還設置 viewonly=True,將發出常規警告,因為這些標誌對於 viewonly=True 關係沒有意義。
  • 為什麼要使用新一代ORM框架sqltoy-orm
    sqltoy-orm是基於java的最綜合最理想的orm框架,主要特點:1、是真正超越hibernate/jpa、jdbcTemplate、mybatis/plus、jooq之和的orm框架,規避了上述框架的走極端的缺點,發揮他們的優勢,即:普通的crud就應該類似於hibernate
  • Python ORM框架SQLAlchemy1.3.20發布
    修復了在 sqlalchemy.exc 模塊中未使用 SQLAlchemy 的標準後期導入系統的功能級別的導入。時,該類型將不查詢架構轉換圖,以查看該類型是否存在。此外,修復了以下問題:如果在單個 DDL 序列中多次遇到相同的枚舉,則「檢查」查詢將重複運行,而不是依賴於緩存的值。MySQL 相關調整了 MySQL 方言,使其能夠正確括號化 MySQL 8 所接受的功能索引表達式。
  • 資料庫:django ORM如何處理N+1查詢
    N+1問題並不是ORM獨有,只是使用orm的時候,資料庫表中的行變成一個對象,於是很自然的就容易使用上面的方法來進行查詢不使用orm進行編程的情況,一般直接用子查詢或者inner joinselect a.
  • SQLAlchemy 1.1.10 發布,Python 的 ORM 框架
    oraclemisc下載地址:https://www.sqlalchemy.org/download.html>>>【全民狂歡,評論有禮】5月15日-31日評論每日更新的「新聞資訊和軟體更新資訊」,評論點讚數超過 20 的可登上每周更新的「源資訊」和「軟體周刊」兩大欄目,點讚數超過 50 的還將獲得
  • 使用Flask構建web項目的代碼架構以及技術棧模板(一)
    前言這篇文章保證新手也能看得懂,看不懂的童鞋不要著急,文章最後我會做一個總結,即項目的整體的運行思路,相信大家看完會一目了然~~Flask十分靈活,可以自己設計代碼的架構,而不像django那樣把代碼的架構設計好了給你使用,這裡說的代碼架構就是我們項目的目錄設計。
  • Ebean ORM 7.12.2 發布,Java 持久層 ORM 框架
    該框架易於使用,簡單易學.自動生成的queryBean代碼,能夠使用類型安全的查詢,示例如下:LocalDate lastWeek = ...    #724 @cache配置queryCaching=true和beanCaching=false當NPE使用L2緩存的時候.
  • Python通過SQLAlchemy-SQL_Expression來訪問操作MySQL資料庫
    本系列中已經對 &34; 進行了介紹。本系列中已經對 &34; 進行了介紹。本系列中已經對 &34; 進行了介紹。它具有幾個不同的功能區域,可以單獨使用或組合使用。SQLAlchemy首次發行於2006年2月,並迅速地在Python社區中最廣泛使用的ORM工具之一,不亞於Django的ORM框架。
  • Python通過"SQLAlchemy-Raw_SQL"方式來訪問操作MySQL資料庫
    本系列中已經對 &34; 進行了介紹。本系列中已經對 &34; 進行了介紹。本系列中已經對 &34; 進行了介紹。它具有幾個不同的功能區域,可以單獨使用或組合使用。SQLAlchemy首次發行於2006年2月,並迅速地在Python社區中最廣泛使用的ORM工具之一,不亞於Django的ORM框架。
  • pony.orm 基礎入門
    一個關係型資料庫包含的是存儲在表中的行。然而,當用高級的面向對象語言編寫程序時,當從資料庫中檢索的數據可以以對象的形式訪問時,就會方便很多。Pony ORM 是 Python語言的一個庫,可以方便地處理以行形式存儲在關係型資料庫中的對象。
  • Pony - 最智能的 Python ORM 框架
    在代碼裡手寫 SQL 並不是一件愉快的事情,故而,代碼中往往使用 ORM,把代碼中定義的數據模型、查詢和數據操作轉換為 SQL 語言來操作資料庫。不同的 ORM,對於資料庫操作的抽象程度也有所不同,抽象程度更高的 ORM,往往能夠寫出更為貼合程式語言的數據操作代碼,且對 SQL 的轉換有更為智能的處理,隱去更多無需關注的底層細節,使代碼變得更為優雅,提高開發效率。
  • spring-data-ebean v1.3.0 發布,超強大的 ORM 框架
    超簡單、超強大的 ORM 框架,OQL、SQL、ES 多查詢引擎,超越 JPA、Hibernate、Mybatis。
  • 我為什麼堅持寫sqltoy-orm開源框架
    3、大型或基礎核心型開源框架而sqltoy-orm則目標是代替hibernate和mybatis,屬於基礎核心框架,一旦使用對項目影響深遠,甚至說影響項目的整個生命周期!所以說這類開源是需要極其慎重的!
  • sagacity-sqltoy 4.17.8 發布,Java ORM 框架
    1、支持緩存翻譯未匹配模板設置為空白或空字符串: uncached-template=""(之前空白當成無效設置,依舊返回xxkey 未匹配)<translate cache="dictCache" cache-type="POST_TYPE" columns="POST_TYPE" uncached-template="" />sqltoy-orm