SqlAlchemy 2.0 中文文档(四)(3)https://developer.aliyun.com/article/1562996
声明式映射
Declarative Mapping 是在现代 SQLAlchemy 中构建映射的典型方式。最常见的模式是首先使用 DeclarativeBase
超类构造一个基类。结果基类,当被子类继承时,将对所有从它继承的子类应用声明式映射过程,相对于默认情况下新基类的本地 registry
。下面的示例说明了使用声明基类然后在声明式表映射中使用它的方法:
from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column # declarative base class class Base(DeclarativeBase): pass # an example mapping using the base class User(Base): __tablename__ = "user" id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] fullname: Mapped[str] = mapped_column(String(30)) nickname: Mapped[Optional[str]]
上文中,DeclarativeBase
类用于生成一个新的基类(在 SQLAlchemy 的文档中通常称为 Base
,但可以使用任何想要的名称),新的映射类可以从中继承,就像上面构造了一个新的映射类 User
一样。
从 2.0 版本开始更改:DeclarativeBase
超类取代了 declarative_base()
函数和 registry.generate_base()
方法的使用;超类方法与 PEP 484 工具集成,无需使用插件。有关迁移说明,请参阅 ORM Declarative Models。
基类指的是一个维护一组相关映射类的 registry
对象,以及一个保留了一组将类映射到其中的 Table
对象的 MetaData
对象。
主要的 Declarative 映射风格在以下各节中进一步详细说明:
- 使用声明基类 - 使用基类进行声明式映射。
- 使用装饰器进行声明式映射(无声明基类) - 使用装饰器进行声明式映射,而不是使用基类。
在 Declarative 映射类的范围内,还有两种声明 Table
元数据的方式。它们包括:
- 带
mapped_column()
的声明式表 - 在映射类内联声明表列,使用mapped_column()
指令(或者在遗留形式中,直接使用Column
对象)。mapped_column()
指令也可以选择性地与类型注解结合使用,使用Mapped
类可以直接提供有关映射列的一些细节。列指令与__tablename__
和可选的__table_args__
类级指令的组合将允许声明式映射过程构造一个要映射的Table
对象。 - 声明式与命令式表(也称为混合声明式) - 不是分别指定表名和属性,而是将明确构造的
Table
对象与否则以声明方式映射的类相关联。这种映射风格是“声明式”和“命令式”映射的混合体,并适用于将类映射到反射的Table
对象,以及将类映射到现有 Core 构造,如连接和子查询。
声明式映射的文档继续在使用声明性映射类 ### 命令式映射
命令式或经典映射是指使用registry.map_imperatively()
方法配置映射类的配置,其中目标类不包含任何声明式类属性。
提示
命令式映射形式是 SQLAlchemy 在 2006 年的最初版本中少用的一种映射形式。它本质上是绕过声明式系统提供一种更“精简”的映射系统,不提供现代特性,如PEP 484支持。因此,大多数文档示例使用声明式形式,并建议新用户从声明式表配置开始。
在 2.0 版中更改:现在使用registry.map_imperatively()
方法创建经典映射。sqlalchemy.orm.mapper()
独立函数被有效删除。
在“经典”形式中,表元数据是分别使用Table
构造创建的,然后通过registry.map_imperatively()
方法与User
类关联,在建立registry
实例之后。通常,一个registry
的单个实例共享所有彼此相关的映射类:
from sqlalchemy import Table, Column, Integer, String, ForeignKey from sqlalchemy.orm import registry mapper_registry = registry() user_table = Table( "user", mapper_registry.metadata, Column("id", Integer, primary_key=True), Column("name", String(50)), Column("fullname", String(50)), Column("nickname", String(12)), ) class User: pass mapper_registry.map_imperatively(User, user_table)
关于映射属性的信息,例如与其他类的关系,通过properties
字典提供。下面的示例说明了第二个Table
对象,映射到名为Address
的类,然后通过relationship()
链接到User
:
address = Table( "address", metadata_obj, Column("id", Integer, primary_key=True), Column("user_id", Integer, ForeignKey("user.id")), Column("email_address", String(50)), ) mapper_registry.map_imperatively( User, user, properties={ "addresses": relationship(Address, backref="user", order_by=address.c.id) }, ) mapper_registry.map_imperatively(Address, address)
请注意,使用命令式方法映射的类与使用声明式方法映射的类完全可互换。两个系统最终创建相同的配置,由一个Table
、用户定义类和一个Mapper
对象组成。当我们谈论“Mapper
的行为”时,这也包括在使用声明式系统时 - 它仍然被使用,只是在幕后。 ### 声明式映射
声明式映射是在现代 SQLAlchemy 中构建映射的典型方式。最常见的模式是首先使用DeclarativeBase
超类构造基类。生成的基类,在其派生的所有子类中应用声明式映射过程,相对于一个默认情况下局部于新基类的registry
。下面的示例说明了使用声明基类的情况,然后在声明表映射中使用它:
from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column # declarative base class class Base(DeclarativeBase): pass # an example mapping using the base class User(Base): __tablename__ = "user" id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] fullname: Mapped[str] = mapped_column(String(30)) nickname: Mapped[Optional[str]]
上面,DeclarativeBase
类用于生成一个新的基类(在 SQLAlchemy 的文档中通常称为Base
,但可以有任何所需的名称),从中新映射类User
构造。
从版本 2.0 开始更改:DeclarativeBase
超类取代了declarative_base()
函数和registry.generate_base()
方法的使用;超类方法集成了PEP 484工具,无需使用插件。有关迁移说明,请参阅 ORM 声明性模型。
基类指的是维护一组相关映射类的registry
对象,以及维护一组映射到这些类的Table
对象的MetaData
对象。
主要的声明性映射样式在以下各节中进一步详细说明:
- 使用声明性基类 - 使用基类的声明性映射。
- 使用装饰器进行声明性映射(无声明性基类) - 使用装饰器而不是基类的声明性映射。
在声明性映射类的范围内,还有两种Table
元数据声明的方式。这些包括:
- 使用
mapped_column()
声明的声明性表格 - 表格列在映射类中使用mapped_column()
指令内联声明(或者在传统形式中,直接使用Column
对象)。mapped_column()
指令还可以选择性地与使用Mapped
类进行类型注释,该类可以直接提供有关映射列的一些详细信息相结合。列指令与__tablename__
以及可选的__table_args__
类级别指令的组合将允许声明性映射过程构造一个要映射的Table
对象。 - 声明式与命令式表格(即混合声明式) - 不是单独指定表名和属性,而是将显式构建的
Table
对象与在其他情况下以声明方式映射的类关联起来。这种映射方式是“声明式”和“命令式”映射的混合体,适用于诸如将类映射到反射的Table
对象,以及将类映射到现有的 Core 构造,如联接和子查询的技术。
声明式映射的文档继续在用声明式映射类中。
命令式映射
命令式或经典映射是指使用registry.map_imperatively()
方法配置映射类的一种方法,其中目标类不包含任何声明式类属性。
提示
命令式映射形式是 SQLAlchemy 最早期发布的较少使用的映射形式。它基本上是绕过声明式系统提供更“简化”的映射系统,并且不提供现代特性,例如PEP 484支持。因此,大多数文档示例使用声明式形式,建议新用户从声明式表格配置开始。
在 2.0 版更改:registry.map_imperatively()
方法现在用于创建经典映射。sqlalchemy.orm.mapper()
独立函数已被有效移除。
在“经典”形式中,表元数据是使用Table
构造单独创建的,然后通过registry.map_imperatively()
方法与User
类关联,在建立 registry
实例后。通常,一个registry
实例共享给所有彼此相关的映射类:
from sqlalchemy import Table, Column, Integer, String, ForeignKey from sqlalchemy.orm import registry mapper_registry = registry() user_table = Table( "user", mapper_registry.metadata, Column("id", Integer, primary_key=True), Column("name", String(50)), Column("fullname", String(50)), Column("nickname", String(12)), ) class User: pass mapper_registry.map_imperatively(User, user_table)
映射属性的信息,如与其他类的关系,通过properties
字典提供。下面的示例说明了第二个Table
对象,映射到名为Address
的类,然后通过relationship()
与User
关联:
address = Table( "address", metadata_obj, Column("id", Integer, primary_key=True), Column("user_id", Integer, ForeignKey("user.id")), Column("email_address", String(50)), ) mapper_registry.map_imperatively( User, user, properties={ "addresses": relationship(Address, backref="user", order_by=address.c.id) }, ) mapper_registry.map_imperatively(Address, address)
注意,使用命令式方法映射的类与使用声明式方法映射的类完全可互换。这两种系统最终都创建相同的配置,包括一个由Table
、用户定义类和一个与之关联的Mapper
对象组成的配置。当我们谈论“Mapper
的行为”时,这也包括使用声明式系统 - 它仍然在幕后使用。
映射类的基本组件
通过所有映射形式,通过传递最终成为Mapper
对象的构造参数,可以通过多种方式配置类的映射。传递给Mapper
的参数来自给定的映射形式,包括传递给registry.map_imperatively()
的参数,用于命令式映射,或者使用声明式系统时,来自与被映射的表列、SQL 表达式和关系相关联的参数以及属性的参数,如 mapper_args。
Mapper
类寻找的四类常规配置信息如下:
待映射的类
这是我们在应用程序中构造的类。通常情况下,这个类的结构没有限制。[1]当一个 Python 类被映射时,该类只能有一个Mapper
对象。[2]
当使用声明式映射风格时,要映射的类要么是声明基类的子类,要么由装饰器或函数处理,如registry.mapped()
。
当使用命令式映射风格时,类直接作为map_imperatively.class_
参数传递。
表格或其他来源子句对象
在绝大多数常见情况下,这是Table
的实例。对于更高级的用例,它还可以指代任何一种FromClause
对象,最常见的替代对象是Subquery
和Join
对象。
当使用声明性映射样式进行映射时,主题表格要么是由声明性系统基于__tablename__
属性和所呈现的Column
对象生成的,要么是通过__table__
属性建立的。这两种配置样式分别在具有映射列的声明性表格和具有命令式表格的声明性(又名混合声明性)中呈现。
当使用命令式样式进行映射时,主题表格作为map_imperatively.local_table
参数按位置传递。
与映射类的“每个类一个映射器”的要求相反,作为映射主题的Table
或其他FromClause
对象可以与任意数量的映射关联。Mapper
直接对用户定义的类进行修改,但不以任何方式修改给定的Table
或其他FromClause
。
属性字典
这是将与映射类关联的所有属性关联起来的字典。默认情况下,Mapper
从给定的Table
生成此字典的条目,形式为每个都引用映射表的单个Column
的ColumnProperty
对象。属性字典还将包含所有其他需要配置的MapperProperty
对象,最常见的是通过relationship()
构造函数生成的实例。
当使用声明式映射样式进行映射时,属性字典是由声明式系统通过扫描要映射的类以获取适当属性而生成的。请参阅使用声明式定义映射属性部分以获取有关此过程的说明。
当使用命令式映射样式进行映射时,属性字典直接作为properties
参数传递给registry.map_imperatively()
,该参数将其传递给Mapper.properties
参数。
其他映射器配置参数
当使用声明式映射样式进行映射时,附加的映射器配置参数通过__mapper_args__
类属性进行配置。使用示例请参见使用声明式定义的映射器配置选项。
当使用命令式映射样式进行映射时,关键字参数传递给registry.map_imperatively()
方法,该方法将其传递给Mapper
类。
接受的全部参数范围在Mapper
中有文档记录。
要映射的类
这是我们应用程序中构建的一个类。通常情况下,此类的结构没有任何限制。[1] 当映射 Python 类时,该类只能有一个Mapper
对象。[2]
当使用声明式映射风格进行映射时,要映射的类是声明基类的子类,或者由装饰器或函数(如registry.mapped()
)处理。
当使用命令式风格进行映射时,类直接作为map_imperatively.class_
参数传递。
表或其他来自子句对象
在绝大多数常见情况下,这是一个Table
的实例。对于更高级的用例,它也可能指的是任何类型的FromClause
对象,最常见的替代对象是Subquery
和Join
对象。
当使用声明式映射风格进行映射时,主题表通过声明系统基于__tablename__
属性和提供的Column
对象生成,或者通过__table__
属性建立。这两种配置样式在使用 mapped_column() 进行声明性表配置和具有命令式表的声明式(也称为混合声明式)中介绍。
当使用命令式风格进行映射时,主题表作为map_imperatively.local_table
参数按位置传递。
与映射类“每个类一个映射器”的要求相反,映射的Table
或其他FromClause
对象可以与任意数量的映射相关联。Mapper
直接将修改应用于用户定义的类,但不以任何方式修改给定的Table
或其他FromClause
。
属性字典
这是一个与映射类相关联的所有属性的字典。默认情况下,Mapper
从给定的Table
中派生此字典的条目,形成每个映射表的Column
的ColumnProperty
对象。属性字典还将包含要配置的所有其他种类的MapperProperty
对象,最常见的是由relationship()
构造生成的实例。
当使用声明性映射风格进行映射时,属性字典由声明性系统通过扫描要映射的类以找到合适的属性而生成。有关此过程的说明,请参见使用声明性定义映射属性部分。
当使用命令式映射风格进行映射时,属性字典直接作为properties
参数传递给registry.map_imperatively()
,它将把它传递给Mapper.properties
参数。
其他映射器配置参数
当使用声明性映射风格进行映射时,额外的映射器配置参数通过__mapper_args__
类属性配置。有关用法示例,请参阅使用声明性配置选项的映射器。
当使用命令式映射风格进行映射时,关键字参数传递给registry.map_imperatively()
方法,该方法将它们传递给Mapper
类。
接受的参数的完整范围在Mapper
中有文档记录。
SqlAlchemy 2.0 中文文档(四)(5)https://developer.aliyun.com/article/1562998