Flask-SQLAlchemy 中多表链接查询(不使用外键)
SQLAlchemy是一个功能强大的ORM。Flask-SQLAlchemy是一个Flask插件,它让我们在 Flask 框架中使用 SQLAlchemy 变得更容易。
本篇介绍我在使用 Flask-SQLAlchemy 2.1 时进行联表查询的一些经验。
表定义
这里有两个表,account 表保存帐号 ID 和昵称,bind 表保存 account 之间的绑定关系。
1 |
# 省略了外键定义,请自行脑补 |
对应的 Model 如下:
1 |
class Account(db.Model): |
关联查询
先来看一个简单的例子:查询 gameuid 1000 账号下绑定的所有帐号。
1 |
|
看一看生成的 SQL 语句:
1 |
|
这里的联表查询使用的是 WHERE 语句。如果希望使用 JOIN 语句,可以这样写:
1 |
|
可以看出,现在生成的 SQL 语句已经使用 JOIN 语句了。但上面的语意有点奇怪,既然已经在 query 中使用了 Bind 和 Account,后面再 join 一次 Account 总觉得有点多余。那么 SQLAlchemy 如何选择 JOIN 的时候谁先谁后呢?看看这个错误就知道了:
1 |
|
这个错误显然说明,query 中参数的顺序很重要,第一个参数所代表的 table 就是 JOIN 时放在前面的那个 table。因此,此处 JOIN 的目标应该是 Account, 而不应该是 Bind 自身。
分页支持
上面的例子已经解决了大多数需求了。我们再来看看分页。在 Flask-SQLAlchemy 中封装了一个paginate方法,可以方便地将查询记录进行分页:
1 |
|
报错的原因是 db.session.query 默认返回的是 orm.Query 对象,这个对象并不包含 paginate 方法。要解决这个问题,需要修改 Flask-SQLAlchemy 的源码。
找到SQLAlchemy
对象的__init__
定义,在其中加入session_options['query_cls'] = BaseQuery
即可:flask-sqlalchemy2.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 的记录。那就是,返回两个 Model 的所有字段:
1 |
|
使用上面的语法直接返回 Account 和 Bind 对象,可以进行更加灵活的操作。
多表查询
要联结超过 2 张以上的表,可以直接在 join 得到的结果之后链式调用 join 。也可以在 filter 的结果后面链式调用 join 。join 和 filter 返回的都是 query 对象,因此可以无限链式调用下去。
写完查询后,应该打印生成的 SQL 语句查看一下有没有性能问题。
https://blog.zengrong.net/post/2656.html
更多相关文章
- PostgreSQL操纵大对象(图片等)
- Recordset记录集对象的属性
- GenericObjectPool对象池异常排查
- Java对象创建的过程及对象的内存布局与访问定位
- request对象和response对象
- 20162305 实验二 Java面向对象程序设计 实验报告
- Java类的加载和对象创建流程的详细分析
- Java第三次作业——面向对象基础(封装)
- NullPointerException: android.support.v4.app.FragmentHostCal