SqlAlchemy 2.0 中文文档(五十六)(3)

简介: SqlAlchemy 2.0 中文文档(五十六)

SqlAlchemy 2.0 中文文档(五十六)(2)https://developer.aliyun.com/article/1563155


2.0 迁移 - ORM 使用

SQLAlchemy 2.0 中最显着的可见变化是使用Session.execute()select()一起运行 ORM 查询,而不是使用Session.query()。正如在其他地方提到的,实际上没有计划真正删除Session.query() API 本身,因为现在它是通过内部使用新 API 来实现的,它将保留为遗留 API,并且两个 API 可以自由使用。

下表介绍了调用形式的一般变化,并链接到每个技术的文档。嵌入部分中的个别迁移注释位于表之后,并可能包含此处未概括的其他注释。

主要 ORM 查询模式概述

|

session.query(User).get(42)

|

session.get(User, 42)
ORM 查询 - get()方法移到 Session

|

session.query(User).all()

|

session.execute(
  select(User)
).scalars().all()
# or
session.scalars(
  select(User)
).all()
使用核心选择统一的 ORM 查询Session.scalars() Result.scalars()

|

session.query(User).\
  filter_by(name="some user").\
  one()


session.execute(
  select(User).
  filter_by(name="some user")
).scalar_one()
使用核心选择统一的 ORM 查询Result.scalar_one()

|

session.query(User).\
  filter_by(name="some user").\
  first()

|

session.scalars(
  select(User).
  filter_by(name="some user").
  limit(1)
).first()
使用核心选择统一的 ORM 查询Result.first()

|

session.query(User).options(
  joinedload(User.addresses)
).all()

|

session.scalars(
  select(User).
  options(
    joinedload(User.addresses)
  )
).unique().all()

|

session.query(User).\
  join(Address).\
  filter(
    Address.email == "e@sa.us"
  ).\
  all()

|

session.execute(
  select(User).
  join(Address).
  where(
    Address.email == "e@sa.us"
  )
).scalars().all()

|

session.query(User).\
  from_statement(
    text("select * from users")
  ).\
  all()

|

session.scalars(
  select(User).
  from_statement(
    text("select * from users")
  )
).all()

|

session.query(User).\
  join(User.addresses).\
  options(
    contains_eager(User.addresses)
  ).\
  populate_existing().all()

|

session.execute(
  select(User)
  .join(User.addresses)
  .options(
    contains_eager(User.addresses)
  )
  .execution_options(
      populate_existing=True
  )
).scalars().all()

|

session.query(User).\
  filter(User.name == "foo").\
  update(
    {"fullname": "Foo Bar"},
    synchronize_session="evaluate"
  )

|

session.execute(
  update(User)
  .where(User.name == "foo")
  .values(fullname="Foo Bar")
  .execution_options(
    synchronize_session="evaluate"
  )
)
启用 ORM 的 INSERT、UPDATE 和 DELETE 语句

|

session.query(User).count()

|

session.scalar(
  select(func.count()).
  select_from(User)
)
# or
session.scalar(
  select(func.count(User.id))
)

ORM 查询与核心选择统一

概要

Query对象(以及BakedQueryShardedQuery扩展)成为长期的遗留对象,被直接使用select()构造与Session.execute()方法取代。从Query返回的对象,以列表形式返回对象或元组,或作为标量 ORM 对象统一地作为Session.execute()返回的Result对象,其接口与 Core 执行一致。

下面是旧代码示例:

session = Session(engine)
# becomes legacy use case
user = session.query(User).filter_by(name="some user").one()
# becomes legacy use case
user = session.query(User).filter_by(name="some user").first()
# becomes legacy use case
user = session.query(User).get(5)
# becomes legacy use case
for user in (
    session.query(User).join(User.addresses).filter(Address.email == "some@email.com")
):
    ...
# becomes legacy use case
users = session.query(User).options(joinedload(User.addresses)).order_by(User.id).all()
# becomes legacy use case
users = session.query(User).from_statement(text("select * from users")).all()
# etc

迁移到 2.0 版本

由于 ORM 应用的绝大部分预计都将使用Query对象,并且Query接口的可用性不影响新接口,该对象将在 2.0 版本中保留,但不再是文档的一部分,也大部分不再受支持。现在,select()构造适用于核心和 ORM 用例,当通过Session.execute()方法调用时,将返回 ORM 导向的结果,即如果请求的是 ORM 对象。

Select()构造添加了许多新方法,以与Query兼容,包括Select.filter()Select.filter_by()、重新设计的Select.join()Select.outerjoin()方法、Select.options()等。其他更多的Query的补充方法,如Query.populate_existing(),通过执行选项实现。

返回结果以Result对象的形式表示,这是 SQLAlchemy ResultProxy对象的新版本,它也添加了许多新方法,以与Query兼容,包括Result.one()Result.all()Result.first()Result.one_or_none()等。

但是,Result 对象需要一些不同的调用模式,首次返回时它将始终返回元组,并且不会在内存中去重结果。为了以 Query 的方式返回单个 ORM 对象,必须首先调用 Result.scalars() 修改器。为了返回唯一对象,就像在使用连接式急加载时所必需的那样,必须首先调用 Result.unique() 修改器。

所有新特性的文档select(),包括执行选项等,位于 ORM 查询指南。

下面是一些迁移到 select() 的示例:

session = Session(engine)
user = session.execute(select(User).filter_by(name="some user")).scalar_one()
# for first(), no LIMIT is applied automatically; add limit(1) if LIMIT
# is desired on the query
user = (
    session.execute(select(User).filter_by(name="some user").limit(1)).scalars().first()
)
# get() moves to the Session directly
user = session.get(User, 5)
for user in session.execute(
    select(User).join(User.addresses).filter(Address.email == "some@email.case")
).scalars():
    ...
# when using joinedload() against collections, use unique() on the result
users = (
    session.execute(select(User).options(joinedload(User.addresses)).order_by(User.id))
    .unique()
    .all()
)
# select() has ORM-ish methods like from_statement() that only work
# if the statement is against ORM entities
users = (
    session.execute(select(User).from_statement(text("select * from users")))
    .scalars()
    .all()
)

讨论

SQLAlchemy 同时具有 select() 构造和一个单独的 Query 对象,它具有极其相似但基本上不兼容的接口,这可能是 SQLAlchemy 中最大的不一致性,这是由于随着时间的推移,小的增量添加累积成了两个主要的不同的 API。

在 SQLAlchemy 的最初版本中,根本不存在 Query 对象。最初的想法是 Mapper 构造本身将能够选择行,并且 Table 对象而不是类将用于在 Core 风格的方法中创建各种条件。Query 是在 SQLAlchemy 的历史中的某个时期,作为用户建议的一个新的、“可构建”的查询对象被接受的。在之前的 SQLAlchemy 中,像 .where() 方法这样的概念,SelectResults 称为 .filter(),是不存在的,而 select() 构造只使用了现在已经弃用的“一次全部”构造样式,该样式已在 select() no longer accepts varied  constructor arguments, columns are passed positionally 中不再接受各种构造函数参数。

随着新方法的推出,该对象逐渐演变成为 Query 对象,随着新功能的添加,例如能够选择单个列、能够一次选择多个实体、能够从 Query 对象而不是从 select 对象构建子查询等。目标是 Query 应该具有 select 的所有功能,即它可以被组合以完全构建 SELECT 语句,不需要显式使用 select()。与此同时,select() 也发展出了像 Select.where()Select.order_by() 这样的“生成”方法。

在现代 SQLAlchemy 中,已经实现了这个目标,这两个对象现在在功能上完全重叠。统一这些对象的主要挑战是 select() 对象需要保持对 ORM 完全不可知。为了实现这一点,大部分来自 Query 的逻辑已经移动到 SQL 编译阶段,其中 ORM 特定的编译器插件接收 Select 构造并按照 ORM 风格的查询解释其内容,然后传递给核心级别的编译器以创建 SQL 字符串。随着新的 SQL 编译缓存系统的出现,大部分这种 ORM 逻辑也被缓存了。

另请参阅

ORM 查询与 select、update、delete 内部统一;2.0 风格执行可用 ### ORM 查询 - get() 方法移到会话

概要

Query.get() 方法仍然保留出于遗留目的,但主要接口现在是 Session.get() 方法:

# legacy usage
user_obj = session.query(User).get(5)

迁移到 2.0

在 1.4 / 2.0 中,Session对象新增了一个新的Session.get()方法:

# 1.4 / 2.0 cross-compatible use
user_obj = session.get(User, 5

讨论

Query对象在 2.0 中将成为传统对象,因为 ORM 查询现在可以使用select()对象。由于Query.get()方法与Session定义了一种特殊的交互,并且甚至不一定会发出查询,因此更适合将其作为Session的一部分,其中它类似于其他“身份”方法,例如refreshmerge

SQLAlchemy 最初包含了 “get()” 来模仿 Hibernate 的 Session.load() 方法。就像经常发生的那样,我们稍微弄错了,因为这个方法实际上更多地与Session有关,而不是编写 SQL 查询。

概要

这指的是诸如Query.join()之类的模式,以及像joinedload()这样的查询选项,它们目前接受字符串属性名称或实际类属性的混合。字符串形式将在 2.0 中全部被移除:

# string use removed
q = session.query(User).join("addresses")
# string use removed
q = session.query(User).options(joinedload("addresses"))
# string use removed
q = session.query(Address).filter(with_parent(u1, "addresses"))

迁移到 2.0

现代 SQLAlchemy 1.x 版本支持推荐的技术,即使用映射属性:

# compatible with all modern SQLAlchemy versions
q = session.query(User).join(User.addresses)
q = session.query(User).options(joinedload(User.addresses))
q = session.query(Address).filter(with_parent(u1, User.addresses))

同样的技术适用于 2.0 风格的使用:

# SQLAlchemy 1.4 / 2.0 cross compatible use
stmt = select(User).join(User.addresses)
result = session.execute(stmt)
stmt = select(User).options(joinedload(User.addresses))
result = session.execute(stmt)
stmt = select(Address).where(with_parent(u1, User.addresses))
result = session.execute(stmt)

讨论

字符串调用形式不明确,并且需要内部额外工作以确定适当的路径并检索正确的映射属性。通过直接传递 ORM 映射属性,不仅需要传递必要的信息,而且属性还是经过类型化的,并且更可能与 IDE 和 pep-484 集成兼容。

ORM 查询 - 使用属性列表进行链接的链式形式,而不是单独调用,已移除

概要

关于 ORM 查询 - 加入 / 加载关系使用属性,而不是字符串的方式将被移除:

# chaining removed
q = session.query(User).join("orders", "items", "keywords")

迁移到 2.0

使用单独的调用Query.join()进行 1.x /2.0 交叉兼容使用:

q = session.query(User).join(User.orders).join(Order.items).join(Item.keywords)

对于 2.0 风格的用法,Select具有与Select.join()相同的行为,还具有一个新的Select.join_from()方法,允许显式左侧:

# 1.4 / 2.0 cross compatible
stmt = select(User).join(User.orders).join(Order.items).join(Item.keywords)
result = session.execute(stmt)
# join_from can also be helpful
stmt = select(User).join_from(User, Order).join_from(Order, Item, Order.items)
result = session.execute(stmt)

讨论

移除属性链接的操作符符合简化方法调用接口的原则,比如Select.join()


SqlAlchemy 2.0 中文文档(五十六)(4)https://developer.aliyun.com/article/1563157

相关文章
|
机器学习/深度学习 数据采集 测试技术
Toad:基于 Python 的标准化评分卡模型(上)
在信贷的风控模型中最常用、最经典的可能要属评分卡了,所谓评分卡就是给信贷客户进行打分,按照不同业务场景可为贷前、贷中、贷后和反欺诈,一般叫做ABCF卡。模型得到分数,通过设置cutoff阈值给出评估结果,结果可直接用于通过或拒绝,或者用于策略应用。
2785 0
Toad:基于 Python 的标准化评分卡模型(上)
|
SQL 存储 API
SqlAlchemy 2.0 中文文档(四十四)(6)
SqlAlchemy 2.0 中文文档(四十四)
416 4
|
存储 JSON API
小红书获取笔记详情API接口的开发、应用与收益。
小红书笔记详情API采用Python与Django框架开发,使用MySQL数据库存储数据。接口通过HTTP GET请求获取笔记详情,返回JSON格式数据,包含笔记内容、作者信息、图片链接等。该API应用于小红书APP内笔记展示和互动功能,并支持第三方平台的内容整合与数据分析,提升用户体验与活跃度,促进品牌合作推广,优化平台运营效率,为平台带来显著收益。
1115 1
|
消息中间件 人工智能 监控
Paimon x StarRocks 助力喜马拉雅直播实时湖仓构建
本文由喜马拉雅直播业务与仓库建设负责人王琛撰写,介绍了喜马拉雅直播业务的数据仓库架构迭代升级。文章重点分享了基于 Flink + Paimon + StarRocks 实现实时湖仓的架构及其成效,通过分钟级别的收入监控、实时榜单生成、流量监测和盈亏预警,大幅提升了运营效率与决策质量,并为未来的业务扩展和 AI 项目打下坚实基础。
796 5
Paimon x StarRocks 助力喜马拉雅直播实时湖仓构建
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(五十七)(3)
SqlAlchemy 2.0 中文文档(五十七)
258 0
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(五十七)(2)
SqlAlchemy 2.0 中文文档(五十七)
273 0
|
存储 缓存 数据库
SqlAlchemy 2.0 中文文档(四十四)(5)
SqlAlchemy 2.0 中文文档(四十四)
401 4
|
存储 SQL 关系型数据库
|
网络安全 流计算 Python
实时计算 Flink版操作报错合集之Flink sql-client 针对kafka的protobuf格式数据建表,报错:java.lang.ClassNotFoundException 如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
604 1
|
Shell
常用 adb 命令
常用 adb 命令
975 2