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. Mac m2使用实现微信小程序抓包

    Mac m2使用实现微信小程序抓包 最近换了MacBook Pro,芯片是M2 Pro,很多东西跟windows是不一样的,所以重新配置相应环境,这里介绍一下微信小程序抓包的方法. 使用burp+pr ...

  2. 腾讯事务处理技术验证系统3TS-Coo模块的项目环境安装使用说明

    本篇文章将详细说明3TS-Coo模板的安装和使用,帮助您快速上手项目 第一部分是简单的基础Docker相关概念,精炼的几句小白话快速理解即可: 第二部分是快速安装项目环境的安装文档,简单几行命令搞定, ...

  3. vue中watch侦听器,deep和immediate的用法

    1.deep深度监听的用法 当监听一个对象时,可能想监听整个对象的变化,而不仅仅是某个属性.但在默认情况下,如果你正在监听formData对象并且修改了formData.username,对应的侦听器 ...

  4. 【RocketMQ】事务实现原理总结

    RocketMQ事务的使用场景 单体架构下的事务 在单体系统的开发过程中,假如某个场景下需要对数据库的多张表进行操作,为了保证数据的一致性,一般会使用事务,将所有的操作全部提交或者在出错的时候全部回滚 ...

  5. Matlab 实现连续PID环节与标记系统-3dB点

    Matlab 实现连续PID环节 连续PID环节传递函数: \[\frac{O(s)}{I(s)} = K_P \cdot \left( 1 + \frac{K_{I}}{s} + K_D\cdot ...

  6. Blackmail

    Blackmail Arthur Hailey The chief house officer, Ogilvie, who had declared he would appear at the Cr ...

  7. Skywalking APM监控系列(一丶.NET5.0+接入Skywalking监听)

    前言 新项目采用的abp vnext的微服务模块化架构,所以把应用的服务拆成了很多独立模块 在初期,我们通过日志还能跟踪到问题, 后期服务越来越多(大约扩充到了十几个),随着调用链路越来越深 ,问题也 ...

  8. [ABC310D] Peaceful Teams 题解

    Peaceful Teams 题目大意 将 \(n\) 个人分成 \(T\) 组,要求每组不能包含敌对的人,问有多少种分法. 思路分析 注意到 \(n,T\) 均很小,考虑爆搜. 注意到直接枚举会枚举 ...

  9. 新手面对安卓6.0以上的版本时出现一个关于文件权限检测的问题,报错为:“无法解析符号 'checkSelfPermission'”,解决办法

    [[注意]:这只是笔者在遇到这个问题时的解决方法,如果对您毫无帮助,请自寻他法!!!] 面对新手:在简单做一个音乐播放程序时,如果面对安卓6.0以上的版本,就会出现一个关于文件权限检测的问题,报错为: ...

  10. Springboot+Mybatis+Mybatisplus 框架中增加自定义分页插件和sql 占位符修改插件

    一.Springboot简介 springboot 是当下最流行的web 框架,Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程 ...