版本: 2.0.36 |发行日期: 2024 年 10 月 15 日


SQLAlchemy 2.0 文档


SQLAlchemy 2.0 文档


家


SQLAlchemy Unified 教程


  • 建立连接 - 引擎

  • 使用事务和 DBAPI

  • 使用数据库元数据¶

    • 使用 Table 对象设置 MetaData

      • 表格的组成部分

      • 声明简单约束

      • 向数据库发出 DDL

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

      • 建立声明性基础

      • 声明映射类

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

    • 表反射

    • 后续步骤

  • 使用数据

  • 使用 ORM 进行数据操作

  • 使用 ORM 相关对象

  • 延伸阅读


家


  • 上一篇:使用事务和 DBAPI

  • 下一篇:处理数据

  • 当前位置:首页

    • SQLAlchemy Unified 教程

  • 本页内容:

    • 使用数据库元数据

      • 使用 Table 对象设置 MetaData

        • 表格的组成部分

        • 声明简单约束

        • 向数据库发出 DDL

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

        • 建立声明性基础

        • 声明映射类

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

      • 表反射

      • 后续步骤


SQLAlchemy 1.4 / 2.0 教程


此页面是 SQLAlchemy Unified Tutorial 的一部分。


Previous: 使用事务和 DBAPI |Next: 使用数据


使用数据库元数据¶


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


SQLAlchemy 中数据库元数据最常见的基础对象称为 MetaData、Table 和 Column。以下部分将说明如何在面向 Core 的样式和面向 ORM 的样式中使用这些对象。


ORM 读者们,请继续关注我们!


与其他部分一样,Core 用户可以跳过 ORM 部分,但 ORM 用户最好从这两个角度熟悉这些对象。在使用 ORM 时,这里讨论的 Table 对象以更间接(也是完全 Python 类型)的方式声明的,但是在 ORM 的配置中仍然有一个 Table 对象。


使用 Table 对象设置 MetaData¶


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


要开始使用 SQLAlchemy 表达式语言,我们希望拥有 Table 对象,这些对象表示我们感兴趣的所有数据库表。Table 是 以编程方式构造,或者直接使用 Table 构造函数,或者通过使用 ORM Mapped 类间接地进行介绍(稍后在使用 ORM 声明式形式定义表元数据中介绍)。还可以选择从现有数据库加载部分或全部表信息,称为反射。


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

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


一旦我们有了 MetaData 对象,我们就可以声明一些 Table 对象。本教程将从经典的 SQLAlchemy 教程模型开始,该模型具有一个名为 user_account 的表,用于存储网站的用户等,以及一个相关的表 address,用于存储与user_account中的行关联的电子邮件地址 桌子。当完全不使用 ORM 声明式模型时,我们会构建每个 Table 对象直接分配,通常将每个对象分配给一个变量,该变量将是我们在应用程序代码中引用 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 table 中,我们将使用 user_table Python 变量来引用它。


何时在程序中创建 MetaData 对象?


为整个应用程序使用单个 MetaData 对象是最常见的情况,在应用程序中的单个位置表示为模块级变量,通常在 “models” 或 “dbschema” 类型的包中。通过以 ORM 为中心的注册表或 Declarative Base 基类,以便在 ORM 和 Core 声明之间共享相同的 MetaData Table 对象。


也可以有多个 MetaData 集合; Table 对象可以引用 Table 对象 在其他集合中不受限制。但是,对于 Table 对象,则位于 练习更直接地将它们设置在一个 MetaData 集合,无论是从声明它们的角度,还是从以正确顺序发出 DDL(即 CREATE 和 DROP)语句的角度来看。


Table 的组成部分¶


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


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


  • Column - 表示数据库表中的列,并将自身分配给 Table 对象。专栏 通常包括 String Name 和 Type Object。 的集合 父 Table 的 Column 对象 通常通过位于 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 的参考和 API 文档 Table and Column 位于 Describes databases with MetaData 中。数据类型的参考文档位于 SQL 数据类型对象中。


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


声明 Simple Constraints¶


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

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


通常显式声明的约束是 与数据库对应的 ForeignKeyConstraint 对象 外键约束。当我们声明彼此相关的表时,SQLAlchemy 使用这些外键约束声明的存在,不仅可以将它们在 CREATE 语句中发送到数据库,还可以帮助构建 SQL 表达式。


只涉及目标表上的单个列的 ForeignKeyConstraint 通常通过 ForeignKey 对象使用列级速记表示法进行声明。 下面我们声明第二个表 address 的地址,该地址将具有引用用户的外键约束 桌子:

>>> 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 表示 参数。


提示


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


在下一节中,我们将为用户发出完成的 DDL,并且 address 表以查看完成的结果。


向数据库发出 DDL¶


我们构建了一个对象结构,它表示数据库中的两个数据库表,从根 MetaData 开始 object,然后转换为两个 Table 对象,每个对象都包含 Column 和 Constraint 的集合 对象。 此对象结构将是大多数作的中心 我们未来会同时使用 Core 和 ORM。


我们可以用这个结构做的第一件有用的事情是将 CREATE TABLE 语句或 DDL 发送到我们的 SQLite 数据库,以便我们可以插入 并从中查询数据。 我们已经拥有实现这一目标所需的所有工具,例如 调用 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 过程还负责以正确的 Sequences 发出 CREATE 语句;上面,FOREIGN KEY 约束依赖于现有的 user 表,因此第二个创建 address 表。在更复杂的依赖关系场景中,FOREIGN KEY 约束也可以在事后使用 ALTER 应用于 table。


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


迁移工具通常是合适的


总体而言,MetaData 的 CREATE / DROP 功能对于测试套件、小型和/或新应用程序以及使用短期数据库的应用程序非常有用。但是,对于应用程序数据库架构的长期管理,基于 SQLAlchemy 构建的架构管理工具(如 Alembic)可能是更好的选择,因为它可以管理和编排随着应用程序设计的变化而逐渐更改固定数据库架构的过程。


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


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


前面的示例说明了 Table 的直接使用 对象,这是 SQLAlchemy 最终引用数据库表的基础 在构造 SQL 表达式时。如前所述,SQLAlchemy ORM 提供了 对于 Table 声明过程周围的 Facade,称为声明性 Table。 Declarative Table 进程完成 与上一节的目标相同,即构建 Table 对象,而且在这个过程中也给了我们一个叫做 ORM 映射类的东西,或者只是 “映射类”。使用 ORM 时,映射类是最常见的 SQL 基础单元,在现代 SQLAlchemy 中也可以非常有效地使用以 Core 为中心的使用。


使用 Declarative Table 的一些好处包括:


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


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


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


本节将说明使用 Declarative Table 构建的上一节的相同 Table 元数据。


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


建立声明式基¶


使用 ORM 时,MetaData 集合仍然存在,但它本身与通常称为声明性基的仅限 ORM 的结构相关联。获取新的 Declarative Base 的最权宜之计是创建一个新类,该类是 SQLAlchemy DeclarativeBase 类的子类:

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


在上面,Base 类就是我们所说的 Declarative Base。当我们创建作为 Base 的子类的新类时,与 适当的类级指令,它们都将分别建立为一个新的 ORM 在类创建时映射类,每个类通常(但不限于)引用特定的 Table 对象。


Declarative Base 是指自动为我们创建的 MetaData 集合,假设我们没有从外部提供一个集合。此 MetaData 集合可通过 DeclarativeBase.metadata 类级属性。当我们创建新的映射类时,它们都将在此 MetaData 集合:

>>> Base.metadata
MetaData()


Declarative Base 还引用了一个名为 registry 的集合,它是 SQLAlchemy ORM 中的中心“映射器配置”单元。虽然很少直接访问,但此对象是 mapper 配置过程的核心,因为一组 ORM 映射类将通过此注册表相互协调。与 MetaData 的情况一样,我们的 Declarative Base 也为我们创建了一个注册表(同样具有传递我们自己的注册表的选项),我们可以通过 DeclarativeBase.registry 类变量访问它:

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


使用注册表进行映射的其他方法


DeclarativeBase 不是映射类的唯一方法,只是最常见的方法。Registry 还提供了其他 Mapper 配置模式,包括面向 Decorator 的 Map 类的命令式方法。还完全支持在映射时创建 Python 数据类。ORM 映射类配置中的参考文档 拥有一切。


声明映射类¶


建立基类后,user_account我们现在可以根据新类 User 和 地址。我们在下面说明了最现代的 Declare 形式,它是从使用特殊类型的 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__ 属性获取。


  • 如前所述,这种形式称为 Declarative Table Configuration。几种替代声明样式之一会让我们直接构建 Table 对象,并将其直接分配给 DeclarativeBase.__table__。这种风格称为 Declarative with Imperative Table。


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


  • 对于数据类型简单且没有其他选项的列,我们可以指示 单独使用映射类型注释,使用简单的 Python 类型,如 int 和 str 表示 Integer 和 String。 自定义 Python 类型在 Declarative 中的解释方式 映射过程非常开放;查看各部分 使用带注释的声明性表(mapped_column() 的类型带注释的形式)和 自定义 type Map for background。


  • 可以根据 Optional[<typ>] 类型注释(或其等效项 <typ> | 无或联合[<typ>, None]). 这 mapped_column.nullable 参数也可以显式使用(并且不必与 Comments 的可选性匹配)。


  • 使用显式键入注释是完全的 可选。我们也可以使用 mapped_column() 而不带注解。 当使用这种形式时,我们会使用更显式的类型对象,例如 Integer 和 String 以及 nullable=False 根据需要在每个 mapped_column() 构造中。


  • 两个附加属性 User.addresses 和 Address.user 定义了一种称为 relationship() 的不同类型的属性,该属性 具有如图所示的 Annotation Aware 配置样式。 这 relationship() 结构在 使用 ORM 相关对象。


  • 如果我们没有声明自己的 __init__() 方法,则会自动为类提供一个方法。此方法的默认形式接受所有属性名称作为可选的关键字参数:

    >>> sandy = User(name="sandy", fullname="Sandy Cheeks")


    要自动生成一个功能齐全的 __init__() 方法,该 提供位置参数以及带有 default 关键字的参数 values 中,DataClasses 功能在 可以使用声明性 Dataclass Mapping。当然,使用显式 __init__() 方法也始终是一个选项。


  • 添加了 __repr__() 方法,以便我们获得可读的字符串输出;这里不需要这些方法。与 __init__() 一样,__repr__() 方法 可以使用 DataClasses 功能。


旧的 Declarative 去哪儿了?


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


为了给“旧”方式的用户提供上下文,仍然可以使用 Column 对象(以及使用 declarative_base() 函数创建基类),并且这些表单将继续受到支持,并且没有计划删除支持。这两个工具被新结构取代的原因首先是为了与 PEP 484 工具顺利集成,包括 VSCode 和 type 等 IDE 检查器,例如 Mypy 和 Pyright,不需要插件。其次 从类型注释中派生声明是 SQLAlchemy 的 与 Python 数据类集成,现在可以 从 Mapping 原生生成。


适用于喜欢 “旧” 方式,但仍希望 IDE 不 错误地报告其声明性映射的输入错误,则 mapped_column() 结构是 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 一起使用。


还有一个将旧式 Declarative 类转换为新样式的示例,可以在 ORM Declarative Models 中看到 在 SQLAlchemy 2.0 中的新增功能指南中。


另请参阅


ORM 映射样式 - 不同 ORM 配置样式的完整背景。


Declarative Mapping - Declarative 类映射概述


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


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


由于我们的 ORM 映射类引用 MetaData 集合中包含的 Table 对象,因此在给定 Declarative Base 使用与前面在 将 DDL 发送到数据库。在我们的例子中,我们已经生成了 user 和 address 表。如果我们还没有这样做,我们可以自由地使用 MetaData 与我们的 ORM 声明式基类相关联,为此,通过访问 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 Reflection (表反射)¶


可选部分


本节只是对 Table Reflection 或如何生成 Table 对象。 教程读者 想要继续编写查询,可以随意跳过本节。


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


提示


不要求必须使用反射才能将 SQLAlchemy 与预先存在的数据库一起使用。SQLAlchemy 应用程序在 Python 中显式声明所有元数据是完全典型的,因此其结构对应于现有数据库。元数据结构也不需要包括表、列或预先存在的数据库中本地应用程序运行不需要的其他约束和构造。


作为反射的示例,我们将创建一个新的 Table object 表示我们在其中手动创建的some_table 对象 本文档的前面部分。 还有一些种类 如何执行此作,但最基本的是构造一个 Table 对象,给定表的名称和 MetaData 集合,而不是指示单个 Column 和 Constraint 对象,将目标 Engine 传递给它 使用 Table.autoload_with 参数:

>>> 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)


另请参阅


有关表和架构反射的更多信息,请参阅 Reflecting Database Objects。


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


下一步¶


现在,我们有一个 SQLite 数据库,其中包含两个表,以及 Core 和 ORM 面向表的构造,我们可以使用它们通过 Connection 和/或 ORM 与这些表进行交互 会话。在以下部分中,我们将说明如何使用这些结构创建、作和选择数据。


SQLAlchemy 1.4 / 2.0 教程


下一教程部分:使用数据


Previous: 使用事务和 DBAPINext: 使用数据

© 版权所有 2007-2025,SQLAlchemy 作者和贡献者。


燃烧!龙和炼金术士形象设计由 Rotem Yaari 创作并慷慨捐赠。


使用 Sphinx 7.2.6 创建。 文档上一次生成时间:美国东部标准时间 2025 年 1 月 7 日星期二下午 01:43:01