概述
SQLAlchemy SQL 工具包和对象关系映射器是一套用于处理数据库和 Python 的综合工具
SQLAlchemy 最重要的两个面向用户的部分是对象关系映射器 (ORM)和Core。
Core 包含了 SQLAlchemy 的 SQL 和数据库集成以及描述服务的广度,其中最突出的是SQL 表达式语言。
SQL 表达式语言是一个独立于 ORM 包的工具包,它提供了一个由可组合对象表示的 SQL 表达式构建系统,然后可以在特定事务的范围内针对目标数据库“执行”这些表达式,返回结果集。通过传递表示这些语句的 SQL 表达式对象以及表示要与每个语句一起使用的参数的字典,可以实现插入、更新和删除(即 DML)。
统一教程
SQLAlchemy 被呈现为两个不同的 API,一个建立在另一个之上。这些 API 被称为Core 和ORM。
SQLAlchemy Core 是 SQLAlchemy 作为“数据库工具包”的基础架构。该库提供了管理数据库连接、与数据库查询和结果交互以及以编程方式构建 SQL 语句的工具。
主要仅限 Core 的部分不会引用 ORM。这些部分中使用的 SQLAlchemy 结构将从 sqlalchemy 命名空间导入。作为主题分类的额外指示,它们还将在右侧包含一个深蓝色边框。在使用 ORM 时,这些概念仍然在起作用,但在用户代码中不那么显式。ORM 用户应该阅读这些部分,但不要期望直接使用这些 API 来编写以 ORM 为中心的代码。
SQLAlchemy ORM 建立在 Core 之上,提供可选的对象关系映射功能。ORM 提供了一个额外的配置层,允许用户定义的 Python 类被映射到数据库表和其他结构,以及一种称为会话的对象持久化机制。然后,它扩展了 Core 级别的 SQL 表达式语言,允许以用户定义的对象来编写和调用 SQL 查询。
建立连接 - 引擎
任何 SQLAlchemy 应用程序的开始都是一个称为 Engine 的对象。此对象充当连接到特定数据库的中心源,既提供一个工厂,又提供一个称为 连接池 的存储空间,用于这些数据库连接。引擎通常是仅为特定数据库服务器创建一次的全局对象,并且使用 URL 字符串进行配置,该 URL 字符串将描述它如何连接到数据库主机或后端
|
|
使用事务和DBAPI
获取连接
从用户角度来看,Engine 对象的唯一目的是提供一个连接到数据库的连接单元,称为 Connection。在直接使用 Core 时,Connection 对象是所有与数据库交互的方式。由于 Connection 代表一个针对数据库的开放资源,我们希望始终将对该对象的使用的范围限制在特定上下文中,而最好的方法是使用 Python 上下文管理器形式,也称为 with 语句
|
|
提交更改
我们发出了两个通常是事务性的 SQL 语句,一个“CREATE TABLE”语句 [1] 和一个参数化的“INSERT”语句(上面的参数化语法将在下面几节中讨论,在 发送多个参数 中)。由于我们希望在块内提交所做的工作,因此我们调用 Connection.commit() 方法来提交事务。在我们在块内调用此方法后,我们可以继续运行更多 SQL 语句,如果我们选择,我们可以再次调用 Connection.commit() 来处理后续语句。SQLAlchemy 将这种风格称为边走边提交。
还有一种提交数据的方式,即我们可以预先将“连接”块声明为事务块。对于这种操作模式,我们使用 Engine.begin() 方法获取连接,而不是使用 Engine.connect() 方法。此方法将同时管理 Connection 的范围,并将所有内容包含在一个事务中,并在成功执行块后执行 COMMIT,或在出现异常时执行 ROLLBACK。这种方式被称为 一次开始
使用数据库元数据
在完成引擎和 SQL 执行后,我们准备开始使用 SQLAlchemy。SQLAlchemy Core 和 ORM 的核心元素是 SQL 表达式语言,它允许以流畅、可组合的方式构建 SQL 查询。这些查询的基础是表示数据库概念(如表和列)的 Python 对象。这些对象统称为 数据库元数据。
SQLAlchemy 中最常见的数据库元数据基础对象是 MetaData、Table 和 Column
使用Table对象设置MetaData
当我们使用关系型数据库时,数据库中用于存储数据的基本结构称为表。在 SQLAlchemy 中,数据库“表”最终由一个名为 Table 的 Python 对象表示。
要开始使用 SQLAlchemy 表达式语言,我们需要构建表示我们想要操作的所有数据库表的 Table 对象。Table 是以编程方式构建的,可以通过直接使用 Table 构造函数,也可以通过使用 ORM Mapped 类(稍后在 使用 ORM 声明式表单定义表元数据 中描述)间接构建。还可以选择从现有数据库加载部分或全部表信息,称为 反射。
无论使用哪种方法,我们总是从一个集合开始,这个集合将是我们放置称为 MetaData 对象的表的集合。这个对象本质上是一个围绕 Python 字典的 facade,它存储一系列以字符串名称为键的 Table 对象。虽然 ORM 提供了一些关于从哪里获取这个集合的选项,但我们始终可以选择直接创建一个,它看起来像
|
|
一旦我们有了 MetaData 对象,我们就可以声明一些 Table 对象
对于整个应用程序使用单个 MetaData 对象是最常见的情况,它被表示为应用程序中某个位置的模块级变量,通常位于“models”或“dbschema”类型的包中。 MetaData 通常通过以 ORM 为中心的 registry 或 Declarative Base 基类访问,因此同一个 MetaData 在 ORM 和 Core 声明的 Table 对象之间共享。
声明简单约束
将DDL发射到数据库
我们构建了一个对象结构,它表示数据库中的两个数据库表,从根 MetaData 对象开始,然后进入两个 Table 对象,每个对象都包含一个 Column 和 Constraint 对象的集合。这个对象结构将成为我们今后使用 Core 和 ORM 执行的大多数操作的核心。
使用ORM声明式表单定义表元数据
Table 对象,它是在构建 SQL 表达式时 SQLAlchemy 最终引用数据库表的底层机制。如前所述,SQLAlchemy ORM 为 Table 声明过程提供了一个外观,称为 声明式表。声明式表过程实现了与上一节中相同的目标,即构建 Table 对象,但在这个过程中,它还为我们提供了其他东西,称为 ORM 映射类,或简称为“映射类”。映射类是使用 ORM 时 SQL 最常见的基石,在现代 SQLAlchemy 中,它也可以非常有效地与以 Core 为中心的用法结合使用
使用 ORM 时,声明 Table 元数据的过程通常与声明 映射 类相结合。映射类是我们想要创建的任何 Python 类,它将具有与数据库表中的列关联的属性。虽然实现这一点的方式有很多种,但最常见的风格被称为 声明式,它允许我们同时声明用户定义的类和 Table 元数据。
建立声明式基类
当使用 ORM 时,MetaData 集合仍然存在,但它本身与一个仅限 ORM 的结构相关联,通常被称为 声明式基类。获取新的声明式基类的最便捷方式是创建一个新的类,该类是 SQLAlchemy DeclarativeBase 类的子类。
|
|
上面,Base 类是我们所说的声明式基类。当我们创建新的类作为 Base 的子类时,结合适当的类级别指令,它们将在类创建时被建立为新的 ORM 映射类,每个类通常(但并非总是)引用一个特定的 Table 对象。
声明式基类引用一个 MetaData 集合,该集合会自动为我们创建,假设我们没有从外部提供。这个 MetaData 集合可以通过 DeclarativeBase.metadata 类级别属性访问。当我们创建新的映射类时,它们将分别引用此 MetaData 集合中的一个 Table。
声明式基类也指代一个名为 registry 的集合,它是 SQLAlchemy ORM 中的中心“映射配置”单元。虽然很少直接访问,但此对象是映射配置过程的核心,因为一组 ORM 映射类将通过此注册表相互协调。与 MetaData 一样,我们的声明式基类也为我们创建了一个 registry(同样可以传递我们自己的 registry),我们可以通过 DeclarativeBase.registry 类变量访问它。
声明映射类
在建立了 Base 类之后,我们现在可以根据新的类 User 和 Address,为 user_account 和 address 表定义 ORM 映射类。我们将在下面说明最现代的声明式形式,它由 PEP 484 类型注解驱动,使用特殊类型 Mapped,它指示将属性映射为特定类型。
|
|
每个类都引用一个 Table 对象,该对象是在声明式映射过程中生成的,其名称是通过将字符串分配给 DeclarativeBase.tablename 属性来确定的。创建类后,此生成的 Table 可从 DeclarativeBase.table 属性获得。
如前所述,这种形式被称为 声明式表配置。几种替代声明风格中的一种将让我们直接构建 Table 对象,并将其 分配 给 DeclarativeBase.table。这种风格被称为 带有命令式表的声明式。
为了在 Table 中指示列,我们使用 mapped_column() 结构,并结合基于 Mapped 类型的类型注解。此对象将生成 Column 对象,这些对象将应用于 Table 的构建。
对于具有简单数据类型且没有其他选项的列,我们可以仅指示 Mapped 类型注解,使用简单的 Python 类型,如 int 和 str 来表示 Integer 和 String。在声明式映射过程中如何解释 Python 类型是开放式的;有关背景信息,请参阅部分 使用带注解的声明式表(mapped_column() 的类型注解形式) 和 自定义类型映射。
根据是否存在 Optional[
使用显式类型注解是完全可选的。我们也可以使用 mapped_column() 而不使用注解。使用这种形式时,我们将使用更明确的类型对象,例如 Integer 和 String,以及根据需要在每个 mapped_column() 结构中使用 nullable=False。
另外两个属性,User.addresses 和 Address.user,定义了一种不同类型的属性,称为 relationship(),它具有与所示类似的注解感知配置样式。relationship() 结构将在 使用 ORM 相关对象 中更详细地讨论。
如果我们没有声明自己的 init() 方法,这些类将自动获得一个 init() 方法。此方法的默认形式接受所有属性名称作为可选关键字参数。
sandy = User(name=“sandy”, fullname=“Sandy Cheeks”) 为了自动生成一个功能齐全的 init() 方法,该方法提供位置参数以及具有默认关键字值的参数,可以在 声明式数据类映射 中介绍的数据类功能中使用。当然,始终可以选择使用显式的 init() 方法。
添加了 repr() 方法,以便我们获得可读的字符串输出;这些方法不需要在这里。与 init() 一样,可以使用 数据类 功能自动生成 repr() 方法。
表反射
表反射是指通过读取数据库的当前状态来生成Table和相关对象的过程
作为反射的示例,我们将创建一个新的Table对象,它代表我们在本文档前面部分手动创建的some_table对象。执行此操作的方式有很多种,但最基本的方式是构造一个Table对象,给出表的名称和它将所属的MetaData集合,然后不是指定单个Column和Constraint对象,而是使用Engine传递目标Table.autoload_with参数。
在整个过程中,some_table对象现在包含有关表中存在的Column对象的信息,并且该对象的使用方式与我们显式声明的Table完全相同。