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 |
>>> db.session.query(Bind.bindid, Bind.fromid, Bind.toid, Account.gameuid, Account.nickname). \ |
看一看生成的 SQL 语句:
1 |
>>> print(db.session.query(Bind.bindid, Bind.fromid, Bind.toid, Account.gameuid, Account.nickname). \ |
这里的联表查询使用的是 WHERE 语句。如果希望使用 JOIN 语句,可以这样写:
1 |
>>> print(db.session.query(Bind.bindid, Account.gameuid, Account.nickname). \ |
可以看出,现在生成的 SQL 语句已经使用 JOIN 语句了。但上面的语意有点奇怪,既然已经在 query 中使用了 Bind 和 Account,后面再 join 一次 Account 总觉得有点多余。那么 SQLAlchemy 如何选择 JOIN 的时候谁先谁后呢?看看这个错误就知道了:
1 |
>>> db.session.query(Bind.bindid, Bind.fromid, Account.gameuid, Account.nickname). \ |
这个错误显然说明,query 中参数的顺序很重要,第一个参数所代表的 table 就是 JOIN 时放在前面的那个 table。因此,此处 JOIN 的目标应该是 Account, 而不应该是 Bind 自身。
分页支持
上面的例子已经解决了大多数需求了。我们再来看看分页。在 Flask-SQLAlchemy 中封装了一个 paginate方法,可以方便地将查询记录进行分页:
1 |
>>> db.session.query(Bind.bindid, Bind.fromid, Account.gameuid, Account.nickname). \ |
报错的原因是 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):
if session_options is None:
|
另一种关联查询语法
在 Flask-SQLAlchemy 提供的 Model 对象中,可以使用 Model.query 这样的语法来直接得到一个查询对象,这是由于 Flask-SQLAlchemy 中存在一个 _QueryProperty 类,每次调用 Model.__get__ 时,会自动生成一个基于当前 session 的 query 对象:
1 |
class _QueryProperty(object):
def __init__(self, sa):
|
使用 Model.query 得到的这个 query 对象可以直接进行 JOIN 操作,得到的结果是 Model 对象。这样就方便多了:
1 |
>>> Account.query.join(Bind, Bind.fromid == Account.gameuid).filter(Bind.toid == 1000).all() |
转换成 SQL 是这样的:
1 |
SELECT account.gameuid AS account_gameuid, account.nickname AS account_nickname |
可以看出,这样的查询结果和使用 db.session.query 并没有什么不同。由于返回的是 Model 对象,使用上可能还更加方便了。
筛选字段
如何使用 Model.query.join 语法得到部分字段呢?这里可以使用 SQLAlchemy 提供的 with_eitities 方法:
1 |
>>> Account.query.join(Bind, Bind.fromid == Account.gameuid). \ |
注意,列表中的项 (2, '玩家10001') 并不是标准的 Python tuple。你如果查看它的类型,会发现一个奇怪的名称: <class 'sqlalchemy.util._collections.result'> 。它是一个 AbstractKeyedTuple 对象,拥有一个 keys() 方法,这样可以很容易将其转换成 dict :
1 |
>>> results = Account.query.join(Bind, Bind.fromid == Account.gameuid). \ |
想了解 AbstractKeyedTuple ,可以看看这篇文档 New KeyedTuple implementation dramatically faster 。
获得多个 Model 的记录
除了筛选字段外,还可以用另一个方法获取多个 Model 的记录。那就是,返回两个 Model 的所有字段:
1 |
>>> db.session.query(Account, Bind).join(Bind, Account.gameuid==Bind.fromid).filter(Bind.toid==1000).all() |
使用上面的语法直接返回 Account 和 Bind 对象,可以进行更加灵活的操作。
多表查询
要联结超过 2 张以上的表,可以直接在 join 得到的结果之后链式调用 join 。也可以在 filter 的结果后面链式调用 join 。join 和 filter 返回的都是 query 对象,因此可以无限链式调用下去。
写完查询后,应该打印生成的 SQL 语句查看一下有没有性能问题。
https://blog.zengrong.net/post/2656.html
Flask-SQLAlchemy 中多表链接查询(不使用外键)的更多相关文章
- sql查询指定表外键约束
//////////////////查询指定表外键约束select a.name as 约束名, object_name(b.parent_object_id) as 外键表, d.name as 外 ...
- flask SQLAlchemy中一对多的关系实现
SQLAlchemy是Python中比较优秀的orm框架,在SQLAlchemy中定义了多种数据库表的对应关系, 其中一对多是一种比较常见的关系.利用flask sqlalchemy实现一对多的关系如 ...
- mysql数据库中的多表查询(内连接,外连接,子查询)
用两个表(a_table.b_table),关联字段a_table.a_id和b_table.b_id来演示一下MySQL的内连接.外连接( 左(外)连接.右(外)连接.全(外)连接). MySQL版 ...
- SQL 数据库 子查询、主外键
子查询,又叫做嵌套查询. 将一个查询语句做为一个结果集供其他SQL语句使用,就像使用普通的表一样,被当作结果集的查询语句被称为子查询. 子查询有两种类型: 一种是只返回一个单值的子查询,这时它可以用在 ...
- Sql中常用的创建表 约束 主外键 增删改查的语句
创建数据库 USE master; GO --日记数据库 create database DiaryBase on ( name=DiaryBase_Dat,--逻辑名称 FILENAME='c:\D ...
- T-SQL中找出一个表的所有外键关联表
二种方法(下例中表名为T_Work) 1.SQL查询系统表 SELECT 主键列ID=b.rkey ,主键列名=(SELECT name FROM syscolumns WHERE colid=b.r ...
- 备忘:MySQL中修改表中某列的数据类型、删除外键约束
-- MySQL中修改表中某列的数据类型 ALTER TABLE [COLUMN] 表名 MODIFY 列名 列定义; -- 删除外键约束 SHOW CREATE TABLE 表名; -- 复制CON ...
- 关于hibernate中映射中有many to one等外键关联时的问题
hibernate中的对象的3种状态的理解及导致报错object references an unsaved transient instance - save the transient insta ...
- sqlserver2008中如何用右键可视化的设置外键
右键->设计 然后打表设计界面打开了然后右键点你要设置与其它表关联的列然后点关系,选择外键表与列然后点保存,就这样
随机推荐
- Emgucv - 下载、安装、配置
工欲善其事,必先利其器. 一.下载 Emgucv学习之前,我们先要搭建好开发环-Emgucv库.VS开发平台. (1)VS开发平台,个人觉得VS2015挺好用的,比如:自定义窗口布局.更优的代码编辑器 ...
- (转)java创建对象的步骤
关于对象的创建过程一般是从new指令(我说的是JVM的层面)开始的(具体请看图1),JVM首先对符号引用进行解析,如果找不到对应的符号引用,那么这个类还没有被加载,因此JVM便会进行类加载过程.符号引 ...
- World Finals 2017爆OJ记
Day-Inf: 去年China-Final一道数据结构题的FB送我进WF. 今年课表意外地满,好几天都是早上8点一直上课上到晚上9点,作业也相对较多.敝队大约每个星期只能训练一个下午,有时候甚至一整 ...
- 秘密袭击 [BZOJ5250] [树形DP]
分析: 听说正解是FFT+线段树合并,然而我并不会... 我们来思考其他的方法. 我们要求的是连通块第k大的和 对于某一个连通块,对答案的贡献=val(Rank.K) 我们不好直接算出每个连通块的Ra ...
- python生成字符画
python生成字符画 这个idea来自于实验楼,非常适合练习PIL的像素处理,更重要的是非常有意思. 环境配置 依赖的第三方库就是PIL(Python Image Library),可以直接使用pi ...
- [原创]浅谈IT人如何做理财规划
[原创]浅谈IT人如何做理财规划 鱼哥博客上多数写的是技术和管理相关,但很少有理财等话题,今天抽空来谈谈IT人如何做理财规划,如果要想学习理财,我想很有名的“标准普尔家庭资产象限图”上值得每个学习和理 ...
- log4j2发送消息至Kafka
title: 自定义log4j2发送日志到Kafka tags: log4j2,kafka 为了给公司的大数据平台提供各项目组的日志,而又使各项目组在改动上无感知.做了一番调研后才发现log4j2默认 ...
- IE中的console.log
部分情况下,IE中如果控制台没有开启,打印console.log可能会报错,一下为兼容方案: if(window.console && console.log) { console.l ...
- double compare 0
因为double类型或float类型都是有精度的,其实都是取的近似值,所以有个误差.和一个很小的数比如0.00000001(1e-8)比较就是为了在这个误差范围内进行比较. 举个例子如double b ...
- 您的第一个C++Builder程序(Hello, world!)
最近有些老旧的项目是C++Builder开发的,虽然和Delphi的IDE的界面和操作十分相似,但是还是找本<C++ Builder 5 Developer's Guide>来看看熟悉下, ...