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对象(以及BakedQuery和ShardedQuery扩展)成为长期的遗留对象,被直接使用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的一部分,其中它类似于其他“身份”方法,例如refresh和merge。

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

相关文章
|
开发框架 JavaScript 前端开发
彻底搞懂Vue中的Mixin混入(保姆级教程)
前言 Vue框架现在基本上已经占据了前端的半壁江山,Vue的数据驱动和组件化的思想深入人心。Vue全家桶可能很多小伙伴都比较熟悉,在工作上也用得比较得心应手。但是今天讲的Vue中这个Mixin的用法我相信还有很多小伙伴不知道或者没有用过,或者有些小伙伴接手别人的Vue项目时看到里面有个Mixin文件夹,也会用,但是一直都是云里雾里的状态,今天我们就好好聊聊Mixin,争取以后不在犯迷糊。
1448 0
彻底搞懂Vue中的Mixin混入(保姆级教程)
|
11月前
|
Rust 监控 大数据
从 Pandas 转向 Polars:新手常见的10 个问题与优化建议
Polars 高性能但易误用,新手常犯10大错误:如滥用 `read_csv`、过早 `collect`、误用 Python 循环等。正确做法是善用惰性计算、表达式向量化、列裁剪、流式聚合,避免频繁物化。掌握这些技巧才能释放其真正性能。
638 4
|
11月前
|
API 双11 数据安全/隐私保护
阿里云邮件推送简单易用、高效稳定的邮件群发服务介绍以及详细收费价格
阿里云邮件推送是基于云计算的高效邮件群发服务,支持事务通知、营销推广等场景,具备高并发、高到达率优势,历经双11考验。提供API接入,无需自建服务器。价格:5万封90元/6个月,50万封840元,100万封1590元,500万封7750元,适合中小企业批量发送需求。
1138 5
|
存储 缓存 文件存储
uv安装python及其依赖的加速方法
国内在使用uv的时候,可能会涉及到装python的速度太慢的问题,为了解决这个问题,可以使用`UV_PYTHON_INSTALL_MIRROR`这个环境变量。除此以外,对于多人协作场景,`UV_CACHE_DIR`也是一个有用的环境变量。本文会介绍这两个变量。
9447 10
|
自然语言处理 前端开发 Java
API管理平台:你用的到底是哪个?
本文介绍了多个API管理和文档工具,包括Apifox、Swagger及其增强版Knife4j和RapiDoc、阿里RAP、去哪儿YApi以及Redoc。这些工具各有特色,适用于不同的开发场景。Apifox提供一体化协作平台,支持API文档、调试、Mock和测试;Swagger结合Knife4j适合Java与前端团队,界面美观且功能丰富;YApi则适用于跨语言开发,支持多种API形式的管理;阿里RAP专注于接口文档管理和Mock服务;Redoc则是开源的现代化API文档浏览器。总结而言,选择工具应根据团队需求和技术栈来决定。
5777 16
|
弹性计算 网络协议 Linux
阿里云主机构建FRP内网穿透家用服务器避坑指南
详述了利用FRP工具搭建内网穿透的流程,包括阿里云ECS的配置、家用服务器的准备、FRP工具的下载与配置,以及通过SSH远程访问本地服务器的解决策略,确保了内外网间的稳定连接。
7271 1
阿里云主机构建FRP内网穿透家用服务器避坑指南
|
JavaScript
JS实现一键点击按钮复制文本
【5月更文挑战第6天】JS实现一键点击按钮复制文本
|
Web App开发 Linux Python
linux上安装selenium环境及测试
该文章提供了在Linux CentOS上安装Selenium环境、Chrome浏览器及Chromedriver的详细步骤,并演示了如何以无头模式进行测试。
1747 0
|
Android开发 Java 数据安全/隐私保护
「移动端」Android平台签名证书(.keystore)生成指南
发布Android APK需签名证书,步骤如下: 1. 安装JRE,如[JRE8](https://www.oracle.com/technetwork/java/javase/downloads/index.html),并添加到环境变量。 2. 使用`keytool -genkey`命令生成证书,例如: ``` keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ``` 输入相关个人信息及密码。
3501 0
「移动端」Android平台签名证书(.keystore)生成指南
|
JSON API 数据安全/隐私保护
python小知识-数据验证和解析神器pydantic
Pydantic是一个Python库,用于数据验证和设置管理,基于类型提示提供数据模型验证。它可以用于用户输入验证、JSON序列化和解析,以及API交互中的数据校验。安装Pydantic可使用`pip install -U pydantic`或`conda install pydantic -c conda-forge`。通过定义BaseModel子类并使用Field进行约束,可以创建数据模型并进行验证。例如,定义User模型验证用户名、邮箱和年龄。Pydantic还支持自定义验证器,允许在字段赋值时执行特定逻辑,如密码强度检查和哈希处理。5月更文挑战第19天
2469 1