https://docs.sqlalchemy.org/en/20/tutorial/data.html

新旧版语法的说明

在2.x的SQLALchemy中,查询语法为:

db.session.execute(db.select(...)) 构造一个查询从数据库中获取数据。

另外:查询是 SQLAlchemy 的功能,因此您需要阅读其有关 select 的教程来了解所有相关信息。

*常使用 Result.scalars() 方法来获取查询结果列表,使用 Result.scalar() 方法来获取单个结果。

1.x语法:

您可能会看到使用 模型类.query 来构建查询。这是一个较旧的查询接口,在 SQLAlchemy 中被视为遗留。

总结1.x和2.x语法区别:

  • 1.x语法:模型类.query()是等同于db.session.query(模型类, <查询语法>)

    因为Flask-SQLAlchemy 向每个模型添加一个 query 对象。用于查询给定模型的实例。如:User.querydb.session.query(User) 的快捷方式。

  • 2.x语法:db.session.execute() 是sqlalchemy 2.x版本后的语法,flask-sqlalchemy3.x版本都是基于sqlalchemy 2.x的语法使用。

    • 2.x语法更加接*我们*时使用sql语句去查询数据。特别要注意的是2.x的各个方法调用都要按顺序,和sql一样,如where要在group_by前调用。
  • 使用模型类.query()语法最大的问题是没有比较完善的语法提示。

  • 旧版本的flask-sqlalchemy是基于sqlalchemy 1.x版本的,但是新版本依旧可以使用旧版本的查询语法。

常用查询语句

1.x和2.x语法相互参考对比:https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#migration-orm-usage

获取全部、一个数据的模型类

# 查看所有对象的所有数据
User.query.all() # flask-sqlalchemy 提供的快捷方式,实际上就是1.x的语法
db.session.query(User).all() # 1.x 语法
db.session.execute(db.select(User)).scalars().all() # 2.x 语法 # 查询第一个用户
User.query.first() # 返回的是用户模型类
db.session.query(User).first()
db.session.execute(db.select(User)).scalar() # 获取一个,如果不止一个则抛出异常
db.session.query(User).one()
# 获取一个,如果一个都没有获取到则返回none
db.session.query(User).one_or_none()

过滤查询filter、where方法

基本语法:

1.x语法:
db.session.query(<模型类>).filter(<表达式>,<表达式>,...).all()|first() 2.x语法:
db.session.execute(sa.select(<模型类>).filter(<表达式>,<表达式>,...)).scalars().all()|scalar() 表达式的组成一般为:
<模型类.字段><表达式><值> 多个表达式等同于你where的条件是and.
还有一种方法就是连续多个filter().filter() 也可以实现and运算。
2.x语法中,filter()方法等同于where()方法。where()更加接*你*时使用sql语句查询的语法。

实例:

# 查询user.id 大于等于3的所有用户
db.session.query(User).filter(User.id >= 3).all()
db.session.execute(sa.select(User).filter(User.id>=3)).scalars().all() # 查询user.id 大于等于3的所有用户(select * from user where user.id>=3 and user.age>10)
db.session.query(User).filter(User.id>=3, User.age>10)).all()
db.session.execute(sa.select(User).filter(User.id>=3, User.age>10)).scalars().all() # 查看user.id不等于1的用户
db.session.execute(sa.select(User).where(User.id != 1)).scalars().all()
# 使用not_()来取反也是可以的
db.session.execute(sa.select(User).where(sa.not_(User.id == 1))).scalars().all()

空值、非空值判断

# 空判断
db.session.query(User).filter(User.gender==None).all()
db.session.query(User).filter(User.gender.is_(None)).all() # 非空判断
db.session.query(User).filter(User.gender!=None).all()
db.session.query(User).filter(User.gender.isnot(None)).all()
db.session.query(User).filter(sa.not_(User.gender==None)).all()

模糊查询like、startswith、endswith

# like(), sql中的%%
pline("like() like表达式")
print(User.query.filter(User.name.like("%g%")).all()) # endswith()
# 实际上用的也是like.., SQL:可以看到只是拼接一个like表达式字符串而已, WHERE (users.name LIKE concat(%(name_1)s, '%%'))
pline("endswith() 字段结尾是否包含指定字符串")
print(db.session.query(User).filter(User.name.endswith("g")).all()) # startswith()
pline("startswith() 字段开头是否包含指定字符串")
print(db.session.query(User).filter(User.name.startswith("w")).all()) # contains()
# 实际上用的也是like.. SQL:WHERE (users.name LIKE concat('%%', %(name_1)s, '%%'))
pline("contains() 字段是否包含指定字符串")
print(User.query.filter(User.name.contains("n")).all())

逻辑运算and_、or_、not_

与运算and_()方法用于将多个条件组合在一起。

# 方式一:直接在filter中使用分号,来给定多个表达式可以实现逻辑and运算
db.session.query(User).filter(User.name.startswith("li"), User.email.startswith("li")).all() # 方式二:使用and_()方法,向方法参数中传递多个表达式
from sqlalchemy import and_
db.session.query(User).filter(sa.and_(User.name.startswith("li"), User.email.startswith("li"))).all()

或运算必须使用or_()方法,这点和django的是一样的,django只能使用Q()对象 然后用 | 连接多个Q对象。

# sql:SELECT * FROM users WHERE users.age > 20 or users.email like 'li%'

# 1.x
User.query.filter(sa.or_(User.age > 20, User.email.startswith("li"))).all() # 2.x
db.session.execute(sa.select(User).where(
sa.or_(
User.age > 20,
User.email.startswith("li")
)
)).scalars().all()

取反not_()

# 查询user.id不大于3的所有用户
# sql: select * from users where not users.id>3 db.session.query(User).filter(sa.not_(User.id > 3)).all() db.session.execute(sa.select(User).where(sa.not_(User.id > 3))).scalars().all()

in_() 、notin_()查询

# 查询用户id在1,2,3集合中的用户
db.session.execute(
sa.select(User).where(
User.id.in_([1, 2, 3])
)
).scalars().all() # 查询用户id不在1,2,3集合中的用户
db.session.execute(
sa.select(User).where(
User.id.notin_([1, 2, 3])
)
).scalars().all()

排序order_by()

# 根据用户id倒序排序
# SELECT * FROM user ORDER BY user.id DESC
db.session.query(User).order_by(User.id.desc()).all()
db.session.query(User).order_by(sa.desc(User.id)).all() # 2.x语法
db.session.execute(sa.select(User).order_by(User.id.desc())).scalars().all()
db.session.execute(sa.select(User).order_by(sa.desc(User.id))).scalars().all()

limit()、offset() 以及slice()

# limit限制返回数
db.session.execute(sa.select(User).limit(2)).scalars().all() # offset 偏移
db.session.execute(sa.select(User).offset(2)).scalars().all() # slice(offset, limit) 这个方法是将limit和offset组合在一起了。表名意思就是切片。
db.session.query(User).order_by(User.name).slice(1, 3).all()
db.session.execute(sa.select(User).order_by(User.name).slice(1, 3)).scalars().all()

分页 pagination对象

没错...sqlalchemy给我们提供了分页查询的对象。

分页对象只能通过SQLAlchemy.paginate() and Query.paginate()方法来创建。返回的对象是Pagination.

Pagination类常用属性:

page: int
当前页码 per_page: int
每页多少条数据 items: list[Any]
当前页面上的项目。迭代分页对象相当于迭代当前页的所有项目。 total: int | None
所有页数的总项目数。也就是所有查询结果的总数量。 pages: int
分页的总页数。 has_prev: bool
是否还有上一页 prev_num: int | None
上一页的页码数,没有上一页就是None has_next: bool
是否还有下一页 next_num: int | None
下一页的页码数,没有下一页就是None

Pagination类常用方法:

prev(*, error_out=False)
查询上一页的 Pagination 对象 next(*, error_out=False)
查询下一页的 Pagination 对象

分页查询实例

# 1.x 语法
# page: 第几页, 默认为1
# per_page: 每页多少条数据,默认为20
# max_per_page: 限制per_page的最大值,默认为100 pn = db.session.query(User).paginate(page=1, per_page=2, max_per_page=10) pn2 = db.paginate(sa.select(User).order_by(User.id.desc()), page=2, per_page=3, max_per_page=10) # 直接迭代pn就等同于迭代当前页中的项目
for item in pn:
print(item) 相等于下面的代码
for item in pn.items:
print(item)

group_by()、having()、聚合函数

group_by()、count()、max()、min()、sum()、avg() 等方法使用

count()

from sqlalchemy import func

## count() 数量统计
# SELECT count(*) FROM user
User.query.count() # count(*)
db.session.query(User).count() # count(*) # 这样只能count某一列
# SELECT count(user.id) FROM user
db.session.scalar(sa.select(func.count(User.id))) # 这样就是count(*)啦
# count(*)是包含null值的。
db.session.scalar(sa.select(func.count()).select_from(User)) # count User records, without
# using a subquery.
db.session.query(func.count(User.id)) # return count of user "id" grouped
# by "name"
db.session.query(func.count(User.id)).\
group_by(User.name) from sqlalchemy import distinct
# count distinct "name" values
# SELECT count(DISTINCT users.name) AS count_1 FROM users
db.session.query(func.count(distinct(User.name)))

group_by()

# 按照用户的性别分组并统计每组的人数
# select users.gender, count(users.gender) from users group by users.gender >>> ret = db.session.query(User.gender, sa.func.count(User.gender)).group_by(User.gender).all()
>>> ret
# 返回的结果是列表:里面嵌套一个Row对象,Row对象类似一个元组
[(0, 4), (1, 6)] >>> type(ret[0])
<class 'sqlalchemy.engine.row.Row'> >>> ret[0]._fields
('gender',)
# 可以通过.的方式获取,但是可以发现并没有count字段的属性,是因为这种都需要自己指定,类似sql中的as
>>> ret[0].gender
0 >>> ret = db.session.query(User.gender, sa.func.count(User.gender).label("count")).group_by(User.gender).all()
>>> ret[0]._fields
('gender', 'count')
>>> ret[0].count
4

sum()、max()、min()、avg()

# max()
db.session.query(User.gender, sa.func.max(User.age).label("max_age")).group_by(User.gender).all() # min()
db.session.query(User.gender, sa.func.min(User.age).label("min_age")).group_by(User.gender).all() # sum()
db.session.query(User.gender, sa.func.sum(User.age).label("sum_age")).group_by(User.gender).all() # avg()
db.session.query(User.gender, sa.func.avg(User.age).label("avg_age")).group_by(User.gender).all()

having() 分组后过滤

# 查看素有用户,按照年龄分组,并统计每个年龄组的总数,要求年龄大于20
# SELECT user.age, count(*) as count FROM user group by user.age having user.age > 20
db.session.execute(sa.select(User.age, sa.func.count("*").label("count")).group_by(User.age).having(User.age > 20)).all()

使用原生SQL语句查询

django中也有,是模型类.objects.raw(<sql语句>)

# 1.x
db.session.query(User).\
from_statement(
text("select * from users")
).\
all() # 2.x
db.session.scalars(
select(User).
from_statement(
text("select * from users")
)
).all() # 还可以使用变量
db.session.query(User).from_statement(
sa.text("select * from users where users.id > :num or users.age > :age").bindparams(num=2, age=30)
).all()

使用变量查询:

 db.session.query(User).filter(sa.text("id>:id")).params(id=1).all()

 db.session.query(User).from_statement(
sa.text("select * from users where users.id > :num or users.age > :age").bindparams(num=2, age=30)
).all()

Flask-SQLAlchemy常用新旧查询语法对比的更多相关文章

  1. 新旧版本功能对比 | v1.5.0 全新升级

    Hi~社区的小伙伴们大家好呀! CloudQuery 最新 1.5.0 社区版本即将于 4月14日 发布,正式上线前,我们迫不及待与大家分享与 v1.4 相比,v1.5.0 在性能和功能上有哪些更新和 ...

  2. Arcgis API For IOS扩展AGSDynamicLayer新旧版API对比

    AGSDynamicLayer(ForSubclassEyesOnly) Category Reference Description This category organizes the meth ...

  3. Why containers? Why should we care? 新旧容器的对比

    https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/ The Old Way to deploy applications ...

  4. 灵活使用 SQLAlchemy 中的 ORM 查询

    之前做查询一直觉得直接拼 SQL 比较方便,用了 SQLAlchemy 的 ORM 查询之后,发现也还可以,还提高了可读性. 这篇文章主要说说 SQLAlchemy 常用的 ORM 查询方式,偏实践. ...

  5. Solr常用查询语法笔记

    1.常用查询 q - 查询字符串,这个是必须的.如果查询所有*:* ,根据指定字段查询(Name:张三 AND Address:北京) fq - (filter query)过虑查询,作用:在q查询符 ...

  6. spring加载配置新旧方式对比

    老方式 1.首先要配置配置文件,如beans.xml,内容如下: <?xml version="1.0" encoding="UTF-8"?> &l ...

  7. 使用Flexbox:新旧语法混用实现最佳浏览器兼容

    Flexbox非常的棒,肯定是未来布局的一种主流.在过去的几年这之中,语法改变了不少,这里有一篇“旧”和“新”新的语法区别教程(如果你对英文不太感兴趣,可以移步阅读中文版本).但是,如果我们把Flex ...

  8. 【Flask】Sqlalchemy 常用数据类型

    ### SQLAlchemy常用数据类型:1. Integer:整形,映射到数据库中是int类型.2. Float:浮点类型,映射到数据库中是float类型.他占据的32位.3. Double:双精度 ...

  9. flask SQLAlchemy中一对多的关系实现

    SQLAlchemy是Python中比较优秀的orm框架,在SQLAlchemy中定义了多种数据库表的对应关系, 其中一对多是一种比较常见的关系.利用flask sqlalchemy实现一对多的关系如 ...

  10. Matlab神经网络函数newff()新旧用法差异

    摘要 在Matlab R2010a版中,如果要创建一个具有两个隐含层.且神经元数分别为5.3的前向BP网络,使用旧的语法可以这样写: net1 = newff(minmax(P), [5 3 1]); ...

随机推荐

  1. 2.2 PE结构:文件头详细解析

    PE结构是Windows系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,DOS头是PE文件开头的一个固定长度的结构体, ...

  2. Win10 误删winsock注册表修复。 winsock.reg

    手贱删除了注册表的winsock项, 导致无法上网. 导入后需要重启电脑才能上网, 这个文件是我在别人电脑里导出来的. 下载地址: https://pan.baidu.com/s/1wH8SdeWsx ...

  3. OCI云主机环境如何上传下载文件

    OCI云主机的连接是使用密钥而非用户密码连接. 之前使用的非主流的一个SSH工具,正常连接主机都没问题,但需要手工输入用户. 可是在选择SFTP时,始终找不到用户名的设置,导致密钥连接的SFTP始终失 ...

  4. 「joisc2016 - D3T2」回転寿司

    题意大概是这样,「每次操作选出区间中的一个 LIS(strictly),满足其开端是极靠近左端点且大于 \(A\) 的位置,答案即这个 LIS 的末尾,做一个轮换后弹出序列末端」. 首先做几个观察. ...

  5. Solution -「YunoOI 2016」镜中的昆虫

    Description Link. 区间推平: 区间数颜色. Solution 考虑无修的情况,我们是采用维护每个数的 \(pre\) 来做的.具体一点就是对于每一个 \(a_{i}\) 维护 \(p ...

  6. Python面向对象——property装饰器、继承(与python2不同点)、多继承(优缺点、Mixins)、属性查找、多继承带来的菱形问题

    文章目录 内容回顾 property装饰器 继承 与python2的差别 多继承 为何要用继承 如何实现继承 属性查找 多继承带来的菱形问题 总结: 作业 内容回顾 1.封装=>整合 人的对象. ...

  7. Keycloak 创建和修改自定义用户信息

    前言 公司在用 Keycloak 作为认证服务器,之前在系统数据库里存的,后来想了想是不是可以在 Keycloak 中存.在网上找的方法大多都是通过 admin 接口去改,但这种方法就需要两种解决方案 ...

  8. 一分钟了解 ChatGPT 语音对话

    一.背景 近期 ChatGPT 推出新的语音和图像功能,可以与用户进行语音对话或基于用户上传的图像进行分析和对话,提供了一种新的.更直观的交互体验.用户可以更轻松地表达自己的需求.提出问题,并获得 C ...

  9. kubeadm 工具部署 kubernetes v1.16.2

    环境准备 3个节点,以下基于 Centos 7.6 系统, 内核版本:3.10.0-957.12.2.e17.x86_64 HOST NODE CPU MEM 192.168.1.111 master ...

  10. 简述location规则优先级-实现域名跳转-不同语言-终端跳转-错误页面返回首页-腾讯公益首页

    1.简述location的常见规则优先级,并且逐个验证: = :精确匹配(必须全部相等) #精准匹配优先级最高 ~ :大小写敏感(正则表达式) #一般使用~*忽略大小写匹配 (正则表达式 有上下区分, ...