SQLAlchemy
The Database Toolkit for Python
Python 的数据库工具包
  • 首页
  • 特性
    • 理念声明
    • 特性概览
    • 用户评价
  • 新闻
  • 文档
    • 当前文档 (版本 2.0)

    • 按版本文档
    • 版本 2.1 (开发中)
    • 版本 2.0
    • 版本 1.4
    • 版本 1.3

    • 讲座和教程
    • 发布内容概览
  • 社区
    • 获取支持
    • 参与
    • 开发
    • 行为准则
    • Github
  • 下载
    • 下载
    • 当前发布系列 (2.0)
    • 维护版本 (1.4)
    • 开发访问
    • 许可证
    • 版本编号
    • 发布状态
发布: 2.0.39 当前版本 | 发布日期: 2025 年 3 月 11 日

SQLAlchemy 2.0 文档

SQLAlchemy 2.0 文档

当前版本

首页 | 下载此文档

SQLAlchemy 统一教程

  • 建立连接 - Engine
  • 使用事务和 DBAPI
  • 使用数据库元数据¶
    • 使用 Table 对象设置 MetaData
      • Table 的组件
      • 声明简单约束
      • 向数据库发出 DDL
    • 使用 ORM 声明形式定义表元数据
      • 建立声明基类
      • 声明映射类
      • 从 ORM 映射向数据库发出 DDL
    • 表反射
    • 下一步
  • 使用数据
  • 使用 ORM 操作数据
  • 使用 ORM 相关对象
  • 进一步阅读

项目版本

  • 2.0.39

首页 | 下载此文档

  • 上一篇: 使用事务和 DBAPI
  • 下一篇: 使用数据
  • 向上: 首页
    • SQLAlchemy 统一教程
  • 本页内容
    • 使用数据库元数据
      • 使用 Table 对象设置 MetaData
        • Table 的组件
        • 声明简单约束
        • 向数据库发出 DDL
      • 使用 ORM 声明形式定义表元数据
        • 建立声明基类
        • 声明映射类
        • 从 ORM 映射向数据库发出 DDL
      • 表反射
      • 下一步

SQLAlchemy 1.4 / 2.0 教程

本页是 SQLAlchemy 统一教程 的一部分。

上一篇: 使用事务和 DBAPI | 下一篇: 使用数据

使用数据库元数据¶

有了引擎和 SQL 执行,我们准备开始一些 Alchemy 了。SQLAlchemy Core 和 ORM 的核心要素是 SQL 表达式语言,它允许流畅、可组合地构建 SQL 查询。这些查询的基础是代表数据库概念(如表和列)的 Python 对象。这些对象统称为 数据库元数据。

SQLAlchemy 中数据库元数据最常见的基石对象被称为 MetaData、 Table 和 Column。以下章节将说明如何在面向 Core 的风格和面向 ORM 的风格中使用这些对象。

ORM 阅读者,请留步!

与其他章节一样,Core 用户可以跳过 ORM 章节,但 ORM 用户最好熟悉这两种视角的对象。此处讨论的 Table 对象在使用 ORM 时以更间接(且完全 Python 类型化)的方式声明,但 ORM 的配置中仍然存在 Table 对象。

使用 Table 对象设置 MetaData¶

当我们使用关系数据库时,数据库中我们从中查询数据的基本数据持有结构被称为表。在 SQLAlchemy 中,数据库“表”最终由一个类似命名的 Python 对象 Table 表示。

要开始使用 SQLAlchemy 表达式语言,我们将需要构造 Table 对象,以表示我们感兴趣的所有数据库表。Table 是以编程方式构造的,可以直接使用 Table 构造函数,也可以间接使用 ORM 映射类(稍后在 使用 ORM 声明形式定义表元数据 中描述)。还可以选择从现有数据库加载部分或全部表信息,称为 反射。

无论使用哪种方法,我们总是从一个集合开始,该集合将是我们放置表的 MetaData 对象。此对象本质上是 Python 字典周围的 外观,该字典存储一系列 Table 对象,这些对象以其字符串名称为键。虽然 ORM 提供了一些选项来获取此集合,但我们始终可以选择直接创建一个,如下所示

>>> from sqlalchemy import MetaData
>>> metadata_obj = MetaData()

一旦我们有了 MetaData 对象,我们就可以声明一些 Table 对象。本教程将从经典的 SQLAlchemy 教程模型开始,该模型有一个名为 user_account 的表,用于存储网站的用户,以及一个相关的表 address,用于存储与 user_account 表中的行关联的电子邮件地址。当完全不使用 ORM 声明模型时,我们直接构造每个 Table 对象,通常将每个对象分配给一个变量,这将是我们如何在应用程序代码中引用表的方式

>>> from sqlalchemy import Table, Column, Integer, String
>>> user_table = Table(
...     "user_account",
...     metadata_obj,
...     Column("id", Integer, primary_key=True),
...     Column("name", String(30)),
...     Column("fullname", String),
... )

在上面的示例中,当我们希望编写代码来引用数据库中的 user_account 表时,我们将使用 user_table Python 变量来引用它。

我在程序中什么时候创建 MetaData 对象?

对于整个应用程序,拥有一个 MetaData 对象是最常见的情况,它在应用程序的单个位置表示为模块级变量,通常在“models”或“dbschema”类型的包中。也很常见的是,MetaData 通过以 ORM 为中心的 registry 或 声明基类 基类访问,以便在 ORM 和 Core 声明的 Table 对象之间共享同一个 MetaData。

也可以有多个 MetaData 集合;Table 对象可以引用其他集合中的 Table 对象,而没有限制。但是,对于彼此相关的 Table 对象组,实际上更直接的做法是将它们设置在一个 MetaData 集合中,无论是从声明它们的角度来看,还是从以正确顺序发出的 DDL(即 CREATE 和 DROP)语句的角度来看。

Table 的组件¶

我们可以观察到,以 Python 编写的 Table 构造类似于 SQL CREATE TABLE 语句;从表名开始,然后列出每个列,其中每个列都有一个名称和数据类型。我们在上面使用的对象是

  • Table - 表示数据库表并将自身分配给 MetaData 集合。

  • Column - 表示数据库表中的列,并将自身分配给 Table 对象。Column 通常包括字符串名称和类型对象。 Column 对象相对于父 Table 的集合通常通过位于 Table.c 的关联数组访问

    >>> user_table.c.name
    Column('name', String(length=30), table=<user_account>)
    
    >>> user_table.c.keys()
    ['id', 'name', 'fullname']
  • Integer, String - 这些类表示 SQL 数据类型,可以传递给 Column,无论是否需要实例化。在上面,我们希望为“name”列提供长度“30”,因此我们实例化了 String(30)。但是对于“id”和“fullname”,我们没有指定这些,因此我们可以发送类本身。

另请参阅

MetaData、 Table 和 Column 的参考和 API 文档位于 使用 MetaData 描述数据库。数据类型的参考文档位于 SQL 数据类型对象。

在接下来的章节中,我们将说明 Table 的基本功能之一,即在特定的数据库连接上生成 DDL。但首先我们将声明第二个 Table。

声明简单约束¶

示例 user_table 中的第一个 Column 包括 Column.primary_key 参数,这是一种简写技术,用于指示此 Column 应该是此表的主键的一部分。主键本身通常是隐式声明的,由 PrimaryKeyConstraint 构造表示,我们可以在 Table.primary_key 属性上看到 Table 对象

>>> user_table.primary_key
PrimaryKeyConstraint(Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False))

最常显式声明的约束是 ForeignKeyConstraint 对象,它对应于数据库 外键约束。当我们声明彼此相关的表时,SQLAlchemy 使用这些外键约束声明的存在,不仅是为了在数据库的 CREATE 语句中发出它们,而且还为了帮助构造 SQL 表达式。

仅涉及目标表上单个列的 ForeignKeyConstraint 通常使用列级简写符号通过 ForeignKey 对象声明。下面我们声明第二个表 address,它将具有引用 user 表的外键约束

>>> from sqlalchemy import ForeignKey
>>> address_table = Table(
...     "address",
...     metadata_obj,
...     Column("id", Integer, primary_key=True),
...     Column("user_id", ForeignKey("user_account.id"), nullable=False),
...     Column("email_address", String, nullable=False),
... )

上面的表还具有第三种约束,在 SQL 中是“NOT NULL”约束,上面使用 Column.nullable 参数指示。

提示

当在 Column 定义中使用 ForeignKey 对象时,我们可以省略该 Column 的数据类型;它是从相关列的数据类型自动推断的,在上面的示例中,是 user_account.id 列的 Integer 数据类型。

在下一节中,我们将发出 user 和 address 表的完整 DDL,以查看完成的结果。

向数据库发出 DDL¶

我们已经构建了一个对象结构,该结构表示数据库中的两个数据库表,从根 MetaData 对象开始,然后进入两个 Table 对象,每个对象都保存着 Column 和 Constraint 对象的集合。此对象结构将成为我们使用 Core 和 ORM 执行的大多数操作的中心。

我们可以使用此结构做的第一个有用的事情是向我们的 SQLite 数据库发出 CREATE TABLE 语句或 DDL,以便我们可以从中插入和查询数据。我们已经拥有了执行此操作所需的所有工具,只需在我们的 MetaData 上调用 MetaData.create_all() 方法,并将引用目标数据库的 Engine 发送给它

>>> metadata_obj.create_all(engine)
BEGIN (implicit) PRAGMA main.table_...info("user_account") ... PRAGMA main.table_...info("address") ... CREATE TABLE user_account ( id INTEGER NOT NULL, name VARCHAR(30), fullname VARCHAR, PRIMARY KEY (id) ) ... CREATE TABLE address ( id INTEGER NOT NULL, user_id INTEGER NOT NULL, email_address VARCHAR NOT NULL, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES user_account (id) ) ... COMMIT

上面的 DDL 创建过程包括一些特定于 SQLite 的 PRAGMA 语句,这些语句在发出 CREATE 之前测试每个表的是否存在。完整的一系列步骤也包含在 BEGIN/COMMIT 对中,以适应事务性 DDL。

创建过程还负责以正确的顺序发出 CREATE 语句;在上面,FOREIGN KEY 约束依赖于 user 表的存在,因此 address 表是第二个创建的。在更复杂的依赖场景中,FOREIGN KEY 约束也可以在使用 ALTER 之后应用于表。

MetaData 对象还具有 MetaData.drop_all() 方法,该方法将以与发出 CREATE 时相反的顺序发出 DROP 语句,以便删除架构元素。

迁移工具通常是合适的

总的来说,MetaData 的 CREATE / DROP 功能对于测试套件、小型和/或新应用程序以及使用短生命周期数据库的应用程序非常有用。但是,对于长期管理应用程序数据库架构,诸如 Alembic 之类的架构管理工具(它建立在 SQLAlchemy 之上)可能是更好的选择,因为它可以管理和协调随着应用程序设计更改而逐步更改固定数据库架构的过程。

使用 ORM 声明形式定义表元数据¶

制作 Table 对象的另一种方法?

前面的示例说明了 Table 对象的直接使用,这是 SQLAlchemy 在构造 SQL 表达式时最终如何引用数据库表的基础。如前所述,SQLAlchemy ORM 提供了围绕 Table 声明过程的外观,称为声明式表。声明式表过程实现了与我们在上一节中相同的目标,即构建 Table 对象,但在此过程中,它还为我们提供了称为 ORM 映射类 或简称为“映射类”的其他内容。映射类是在使用 ORM 时 SQL 最常见的基石单元,并且在现代 SQLAlchemy 中也可以非常有效地与以 Core 为中心的使用结合使用。

使用声明式表的一些好处包括

  • 一种更简洁和 Pythonic 的设置列定义样式,其中 Python 类型可以用于表示要在数据库中使用的 SQL 类型

  • 生成的映射类可用于形成 SQL 表达式,这些表达式在许多情况下维护 PEP 484 类型信息,这些信息由静态分析工具(如 Mypy 和 IDE 类型检查器)拾取

  • 允许一次性声明表元数据和持久性/对象加载操作中使用的 ORM 映射类。

本节将说明使用声明式表构造的先前章节中相同的 Table 元数据。

当使用 ORM 时,我们声明 Table 元数据的过程通常与声明 映射 类的过程相结合。映射类是我们想要创建的任何 Python 类,它将具有与其关联的属性,这些属性将链接到数据库表中的列。虽然实现方式有几种,但最常见的样式称为 声明式,它允许我们一次性声明用户定义的类和 Table 元数据。

建立声明基类¶

当使用 ORM 时,MetaData 集合仍然存在,但它本身与通常称为声明基类的仅 ORM 构造关联。获取新的声明基类最便捷的方法是创建一个新的类,该类继承 SQLAlchemy DeclarativeBase 类

>>> from sqlalchemy.orm import DeclarativeBase
>>> class Base(DeclarativeBase):
...     pass

上面,Base 类是我们将调用的声明基类。当我们创建 Base 的子类的新类时,结合适当的类级指令,它们将在类创建时各自建立为新的 ORM 映射类,每个类通常(但不完全是)引用特定的 Table 对象。

声明基类引用为我们自动创建的 MetaData 集合,假设我们没有从外部提供一个。此 MetaData 集合可通过 DeclarativeBase.metadata 类级属性访问。当我们创建新的映射类时,它们每个都将引用此 MetaData 集合中的 Table

>>> Base.metadata
MetaData()

声明式基类也指一个名为 registry 的集合,它是 SQLAlchemy ORM 中的中心“映射器配置”单元。虽然很少直接访问,但此对象对于映射器配置过程至关重要,因为一组 ORM 映射类将通过此注册表相互协调。与 MetaData 的情况一样,我们的声明式基类也为我们创建了一个 registry(同样可以使用选项传递我们自己的 registry),我们可以通过 DeclarativeBase.registry 类变量访问它

>>> Base.registry
<sqlalchemy.orm.decl_api.registry object at 0x...>

使用 registry 进行映射的其他方式

DeclarativeBase 不是映射类的唯一方法,只是最常见的方法。registry 还提供了其他映射器配置模式,包括面向装饰器和命令式的方式来映射类。还完全支持在映射时创建 Python 数据类。ORM 映射类配置 的参考文档包含了所有内容。

声明映射类¶

建立 Base 类后,我们现在可以根据新类 User 和 Address 为 user_account 和 address 表定义 ORM 映射类。我们在下面说明了声明式的最现代形式,它由 PEP 484 类型注解驱动,使用特殊的类型 Mapped,它指示要映射为特定类型的属性

>>> from typing import List
>>> from typing import Optional
>>> from sqlalchemy.orm import Mapped
>>> from sqlalchemy.orm import mapped_column
>>> from sqlalchemy.orm import relationship

>>> class User(Base):
...     __tablename__ = "user_account"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     name: Mapped[str] = mapped_column(String(30))
...     fullname: Mapped[Optional[str]]
...
...     addresses: Mapped[List["Address"]] = relationship(back_populates="user")
...
...     def __repr__(self) -> str:
...         return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"

>>> class Address(Base):
...     __tablename__ = "address"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     email_address: Mapped[str]
...     user_id = mapped_column(ForeignKey("user_account.id"))
...
...     user: Mapped[User] = relationship(back_populates="addresses")
...
...     def __repr__(self) -> str:
...         return f"Address(id={self.id!r}, email_address={self.email_address!r})"

上面的两个类 User 和 Address 现在被称为 ORM 映射类,可用于 ORM 持久性和查询操作,这将在后面描述。关于这些类的详细信息包括

  • 每个类都引用一个 Table 对象,该对象是在声明式映射过程中生成的,通过将字符串分配给 DeclarativeBase.__tablename__ 属性来命名。创建类后,此生成的 Table 可从 DeclarativeBase.__table__ 属性获得。

  • 如前所述,这种形式被称为 声明式表配置。几种替代声明样式之一将让我们直接构建 Table 对象,并将其直接分配给 DeclarativeBase.__table__。这种样式被称为 带有命令式表的声明式。

  • 为了指示 Table 中的列,我们使用 mapped_column() 构造,并结合基于 Mapped 类型的类型注解。此对象将生成应用于 Table 构造的 Column 对象。

  • 对于具有简单数据类型且没有其他选项的列,我们可以仅指示 Mapped 类型注解,使用简单的 Python 类型(如 int 和 str)来表示 Integer 和 String。在声明式映射过程中,Python 类型如何解释具有非常开放的结尾;有关背景信息,请参阅 使用带注解的声明式表(mapped_column() 的类型注解形式) 和 自定义类型映射 部分。

  • 可以根据 Optional[<typ>] 类型注解(或其等效项 <typ> | None 或 Union[<typ>, None])的存在将列声明为“可为空”或“非空”。也可以显式使用 mapped_column.nullable 参数(并且不必与注解的可选性匹配)。

  • 显式类型注解的使用是完全可选的。我们也可以在没有注解的情况下使用 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__() 方法。

旧的声明式去哪儿了?

SQLAlchemy 1.4 或更早版本的用户会注意到,上面的映射使用了与以前截然不同的形式;声明式映射中不仅使用了 mapped_column() 而不是 Column,而且还使用 Python 类型注解来派生列信息。

为了给“旧”方式的用户提供上下文,仍然可以使用 Column 对象(以及使用 declarative_base() 函数创建基类)进行声明式映射,并且这些形式将继续受到支持,没有计划取消支持。这些新结构取代这两种工具的首要原因是与 PEP 484 工具(包括 VSCode 等 IDE 以及 Mypy 和 Pyright 等类型检查器)顺利集成,而无需插件。其次,从类型注解派生声明是 SQLAlchemy 与 Python 数据类集成的一部分,现在可以从映射中 原生生成。

对于喜欢“旧”方式,但仍然希望他们的 IDE 不会错误地报告其声明式映射的类型错误的用户,mapped_column() 构造是 ORM 声明式映射中 Column 的直接替代品(请注意,mapped_column() 仅用于 ORM 声明式映射;它不能在 Table 构造中使用),并且类型注解是可选的。我们的上述映射可以在没有注解的情况下编写为

class User(Base):
    __tablename__ = "user_account"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(30), nullable=False)
    fullname = mapped_column(String)

    addresses = relationship("Address", back_populates="user")

    # ... definition continues

上面的类比直接使用 Column 的类具有优势,因为 User 类以及 User 的实例将向类型工具指示正确的类型信息,而无需使用插件。mapped_column() 还允许使用其他 ORM 特定的参数来配置行为,例如延迟列加载,这以前需要单独的 deferred() 函数与 Column 一起使用。

还有一个将旧式声明式类转换为新样式的示例,可以在 ORM 声明式模型 中的 SQLAlchemy 2.0 中的新功能? 指南中看到。

另请参阅

ORM 映射风格 - 关于不同 ORM 配置风格的完整背景信息。

声明式映射 - 声明式类映射概述

带有 mapped_column() 的声明式表 - 详细介绍如何使用 mapped_column() 和 Mapped 来定义在使用声明式时要映射的 Table 中的列。

从 ORM 映射向数据库发出 DDL¶

由于我们的 ORM 映射类引用包含在 MetaData 集合中的 Table 对象,因此给定声明式基类发出 DDL 使用与之前在 向数据库发出 DDL 中描述的过程相同的过程。在我们的例子中,我们已经在 SQLite 数据库中生成了 user 和 address 表。如果我们尚未这样做,我们可以自由地使用与我们的 ORM 声明式基类关联的 MetaData 来执行此操作,方法是从 DeclarativeBase.metadata 属性访问集合,然后像以前一样使用 MetaData.create_all()。在这种情况下,运行了 PRAGMA 语句,但由于发现表已存在,因此未生成新表

>>> Base.metadata.create_all(engine)
BEGIN (implicit) PRAGMA main.table_...info("user_account") ... PRAGMA main.table_...info("address") ... COMMIT

表反射¶

可选章节

本节只是对 表反射 相关主题的简要介绍,或者如何从现有数据库自动生成 Table 对象。想要继续编写查询的教程读者可以随意跳过本节。

为了总结关于使用表元数据的章节,我们将说明本节开头提到的另一个操作,即 表反射。表反射是指通过读取数据库的当前状态来生成 Table 和相关对象的过程。在前面的章节中,我们一直在 Python 中声明 Table 对象,然后可以选择向数据库发出 DDL 以生成这样的模式,而反射过程反向执行这两个步骤,从现有数据库开始,并生成 Python 中的数据结构来表示该数据库中的模式。

提示

为了将 SQLAlchemy 与预先存在的数据库一起使用,不需要必须使用反射。典型的做法是 SQLAlchemy 应用程序在 Python 中显式声明所有元数据,使其结构与现有数据库的结构相对应。元数据结构也不需要包含预先存在的数据库中本地应用程序不需要的表、列或其他约束和构造。

作为反射的示例,我们将创建一个新的 Table 对象,该对象表示我们在本文档前面部分手动创建的 some_table 对象。执行此操作的方法再次有一些变体,但最基本的方法是构造一个 Table 对象,给定表的名称和它将所属的 MetaData 集合,然后不是指示单个 Column 和 Constraint 对象,而是使用 Table.autoload_with 参数将其传递给目标 Engine

>>> some_table = Table("some_table", metadata_obj, autoload_with=engine)
BEGIN (implicit) PRAGMA main.table_...info("some_table") [raw sql] () SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE name = ? AND type in ('table', 'view') [raw sql] ('some_table',) PRAGMA main.foreign_key_list("some_table") ... PRAGMA main.index_list("some_table") ... ROLLBACK

在该过程结束时,some_table 对象现在包含有关表中存在的 Column 对象的信息,并且该对象的使用方式与我们显式声明的 Table 完全相同

>>> some_table
Table('some_table', MetaData(),
    Column('x', INTEGER(), table=<some_table>),
    Column('y', INTEGER(), table=<some_table>),
    schema=None)

另请参阅

在 反射数据库对象 中阅读有关表和模式反射的更多信息。

对于与 ORM 相关的表反射变体,使用反射表进行声明式映射 部分包括可用选项的概述。

下一步¶

我们现在有一个 SQLite 数据库已准备就绪,其中包含两个表,以及核心和 ORM 面向表的构造,我们可以使用它们通过 Connection 和/或 ORM Session 与这些表进行交互。在以下各节中,我们将说明如何使用这些结构创建、操作和选择数据。

SQLAlchemy 1.4 / 2.0 教程

下一个教程章节:使用数据

上一篇:使用事务和 DBAPI 下一篇:使用数据
© 版权 2007-2025,SQLAlchemy 作者和贡献者。

flambé! 龙和 炼金术士 图像设计由 Rotem Yaari 创建并慷慨捐赠。

使用 Sphinx 7.2.6 创建。文档最后生成时间:2025 年 3 月 11 日星期二下午 02:40:17 EDT
Python

网站内容版权 © 归 SQLAlchemy 作者和贡献者所有。SQLAlchemy 及其文档在 MIT 许可证下获得许可。

SQLAlchemy 是 Michael Bayer 的商标。mike(&)zzzcomputing.com 保留所有权利。

网站由 zeekofile 生成,非常感谢 Blogofile 项目。

Mastodon Mastodon