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. pycurl库使用详解

    要使用pycurl库 要初始化一个curl对象 c = pycurl.Curl() 设置选项 c.setopt

  2. JavaWeb项目开发环境搭建

    1. 安装JDK1.8 2. 安装Tomcat8 此处安装解压版apache-tomcat-8.0.47,直接将压缩包解压到指定目录即可.例如,D:\apache-tomcat-8.0.47 3. 安 ...

  3. Go协程揭秘:轻量、并发与性能的完美结合

    Go协程为并发编程提供了强大的工具,结合轻量级.高效的特点,为开发者带来了独特的编程体验.本文深入探讨了Go协程的基本原理.同步机制.高级用法及其性能与最佳实践,旨在为读者提供全面.深入的理解和应用指 ...

  4. elementui vue表单提交向别的组件传参失效 路由传参格式

    目录 表单提交向别的组件传参失效 路由传参格式 表单提交向别的组件传参失效 methods: { submitForm(formName) { this.$refs[formName].validat ...

  5. 西门子Teamcenter 许可分析

    西门子Teamcenter 许可 绑定了主机名称,mac地址 另外,Teamcenter可以支持多个许可服务 所以.......................找个正式许可复制就可以 end succ ...

  6. strimzi实战之一:简介和准备

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于strimzi strimzi是一个开源项目,已加 ...

  7. Linux下安装MySQL问题及报错解决

    前言: 在Linux环境下,安装MySQL服务 环境: 虚拟机CentOS7 \-----------------------------------------------\ 流程: 确保mysql ...

  8. html表单与框架

    1.以form开头 其中常用的属性有 action=""  method=""  enctype=""   name="" ...

  9. 如何优雅重启 kubernetes 的 Pod

    最近在升级服务网格 Istio,升级后有个必要的流程就是需要重启数据面的所有的 Pod,也就是业务的 Pod,这样才能将这些 Pod 的 sidecar 更新为新版本. 方案 1 因为我们不同环境的 ...

  10. 虹科干货 | 虹科Redis企业版数据库的延迟如此之小,proxy功不可没!

    在Redis企业版集群的后台发生了许多事件,proxy(代理)隐藏了数据库客户端的所有活动. 大多数开发人员在构建应用程序时都会从小规模开始,使用简单的Redis开源(Redis OSS)数据库.在初 ...