SQLAlchemy 是一個功能強大的 ORM 。 Flask-SQLAlchemy 是一個 Flask 插件,它讓咱們在 Flask 框架中使用 SQLAlchemy 變得更容易。html
本篇介紹我在使用 Flask-SQLAlchemy 2.1 時進行聯表查詢的一些經驗。python
這裏有兩個表,account 表保存賬號 ID 和暱稱,bind 表保存 account 之間的綁定關係。git
1 |
# 省略了外鍵定義,請自行腦補 |
對應的 Model 以下:github
1 |
class Account(db.Model): |
先來看一個簡單的例子:查詢 gameuid 1000 帳號下綁定的全部賬號。sql
1 |
|
看一看生成的 SQL 語句:flask
1 |
|
這裏的聯表查詢使用的是 WHERE 語句。若是但願使用 JOIN 語句,能夠這樣寫:api
1 |
|
能夠看出,如今生成的 SQL 語句已經使用 JOIN 語句了。但上面的語意有點奇怪,既然已經在 query 中使用了 Bind 和 Account,後面再 join 一次 Account 總以爲有點多餘。那麼 SQLAlchemy 如何選擇 JOIN 的時候誰先誰後呢?看看這個錯誤就知道了:session
1 |
|
這個錯誤顯然說明,query 中參數的順序很重要,第一個參數所表明的 table 就是 JOIN 時放在前面的那個 table。所以,此處 JOIN 的目標應該是 Account, 而不該該是 Bind 自身。app
上面的例子已經解決了大多數需求了。咱們再來看看分頁。在 Flask-SQLAlchemy 中封裝了一個 paginate方法,能夠方便地將查詢記錄進行分頁:框架
1 |
|
報錯的緣由是 db.session.query 默認返回的是 orm.Query 對象,這個對象並不包含 paginate 方法。要解決這個問題,須要修改 Flask-SQLAlchemy 的源碼。
找到 SQLAlchemy
對象的 __init__
定義,在其中加入 session_options['query_cls'] = BaseQuery
便可:flask-sqlalchemy 2.3.2 版本支持的,不用修改源碼了!!!
1 |
def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None): |
在 Flask-SQLAlchemy 提供的 Model 對象中,可使用 Model.query
這樣的語法來直接獲得一個查詢對象,這是因爲 Flask-SQLAlchemy
中存在一個 _QueryProperty
類,每次調用 Model.__get__
時,會自動生成一個基於當前 session 的 query 對象:
1 |
class _QueryProperty(object): |
使用 Model.query
獲得的這個 query 對象能夠直接進行 JOIN 操做,獲得的結果是 Model 對象。這樣就方便多了:
1 |
|
轉換成 SQL 是這樣的:
1 |
SELECT account.gameuid AS account_gameuid, account.nickname AS account_nickname |
能夠看出,這樣的查詢結果和使用 db.session.query
並無什麼不一樣。因爲返回的是 Model 對象,使用上可能還更加方便了。
如何使用 Model.query.join
語法獲得部分字段呢?這裏可使用 SQLAlchemy
提供的 with_eitities 方法:
1 |
|
注意,列表中的項 (2, '玩家10001')
並非標準的 Python tuple。你若是查看它的類型,會發現一個奇怪的名稱: <class 'sqlalchemy.util._collections.result'>
。它是一個 AbstractKeyedTuple 對象,擁有一個 keys()
方法,這樣能夠很容易將其轉換成 dict :
1 |
|
想了解 AbstractKeyedTuple ,能夠看看這篇文檔 New KeyedTuple implementation dramatically faster 。
除了篩選字段外,還能夠用另外一個方法獲取多個 Model 的記錄。那就是,返回兩個 Model 的全部字段:
1 |
|
使用上面的語法直接返回 Account 和 Bind 對象,能夠進行更加靈活的操做。
要聯結超過 2 張以上的表,能夠直接在 join 獲得的結果以後鏈式調用 join 。也能夠在 filter 的結果後面鏈式調用 join 。join 和 filter 返回的都是 query 對象,所以能夠無限鏈式調用下去。
寫完查詢後,應該打印生成的 SQL 語句查看一下有沒有性能問題。
https://blog.zengrong.net/post/2656.html