SQLAlchemy 2.0 文档
复合列类型¶
列的集合可以与单个用户定义的数据类型关联,在现代用法中,这通常是一个 Python dataclass。ORM 提供一个单一属性,它使用您提供的类来表示列组。
一个简单的例子是将一对 Integer
列表示为一个 Point
对象,其属性为 .x
和 .y
。使用 dataclass,这些属性使用相应的 int
Python 类型定义
import dataclasses
@dataclasses.dataclass
class Point:
x: int
y: int
也接受非 dataclass 形式,但需要实现其他方法。有关使用非 dataclass 类的示例,请参阅 使用旧式的非数据类 部分。
2.0 版本新增: composite()
构造完全支持 Python dataclasses,包括从复合类派生映射列数据类型的能力。
我们将创建一个到表 vertices
的映射,它将两个点表示为 x1/y1
和 x2/y2
。Point
类使用 composite()
构造与映射列关联。
下面的示例说明了 composite()
的最现代形式,它与完全 注解声明式表 配置一起使用。mapped_column()
构造表示每个列,直接传递给 composite()
,指示要生成的列的零个或多个方面,在本例中为名称;composite()
构造直接从 dataclass 派生列类型(在本例中为 int
,对应于 Integer
)
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy.orm import composite, mapped_column
class Base(DeclarativeBase):
pass
class Vertex(Base):
__tablename__ = "vertices"
id: Mapped[int] = mapped_column(primary_key=True)
start: Mapped[Point] = composite(mapped_column("x1"), mapped_column("y1"))
end: Mapped[Point] = composite(mapped_column("x2"), mapped_column("y2"))
def __repr__(self):
return f"Vertex(start={self.start}, end={self.end})"
提示
在上面的示例中,表示复合类型的列(x1
、y1
等)也可以在类上访问,但类型检查器无法正确理解它们。如果访问单个列很重要,则可以显式声明它们,如 直接映射列,将属性名称传递给 composite 中所示。
上面的映射将对应于一个 CREATE TABLE 语句,如下所示
>>> from sqlalchemy.schema import CreateTable
>>> print(CreateTable(Vertex.__table__))
CREATE TABLE vertices (
id INTEGER NOT NULL,
x1 INTEGER NOT NULL,
y1 INTEGER NOT NULL,
x2 INTEGER NOT NULL,
y2 INTEGER NOT NULL,
PRIMARY KEY (id)
)
使用映射的复合列类型¶
使用顶部部分所示的映射,我们可以使用 Vertex
类,其中 .start
和 .end
属性将透明地引用 Point
类引用的列,以及使用 Vertex
类的实例,其中 .start
和 .end
属性将引用 Point
类的实例。x1
、y1
、x2
和 y2
列被透明地处理
持久化 Point 对象
我们可以创建一个
Vertex
对象,将Point
对象赋值为成员,它们将按预期持久化>>> v = Vertex(start=Point(3, 4), end=Point(5, 6)) >>> session.add(v) >>> session.commit()
BEGIN (implicit) INSERT INTO vertices (x1, y1, x2, y2) VALUES (?, ?, ?, ?) [generated in ...] (3, 4, 5, 6) COMMIT选择 Point 对象作为列
composite()
将允许Vertex.start
和Vertex.end
属性在使用 ORMSession
(包括旧式Query
对象)选择Point
对象时,尽可能地像单个 SQL 表达式一样工作>>> stmt = select(Vertex.start, Vertex.end) >>> session.execute(stmt).all()
SELECT vertices.x1, vertices.y1, vertices.x2, vertices.y2 FROM vertices [...] ()[(Point(x=3, y=4), Point(x=5, y=6))]在 SQL 表达式中比较 Point 对象
Vertex.start
和Vertex.end
属性可以在 WHERE 条件和类似条件中使用,使用临时的Point
对象进行比较>>> stmt = select(Vertex).where(Vertex.start == Point(3, 4)).where(Vertex.end < Point(7, 8)) >>> session.scalars(stmt).all()
SELECT vertices.id, vertices.x1, vertices.y1, vertices.x2, vertices.y2 FROM vertices WHERE vertices.x1 = ? AND vertices.y1 = ? AND vertices.x2 < ? AND vertices.y2 < ? [...] (3, 4, 7, 8)[Vertex(Point(x=3, y=4), Point(x=5, y=6))]2.0 版本新增:
composite()
构造现在支持“排序”比较,例如<
、>=
和类似比较,以及已经存在的对==
、!=
的支持。提示
上面使用“小于”运算符 (
<
) 的“排序”比较以及使用==
的“相等”比较在用于生成 SQL 表达式时,由Comparator
类实现,并且不使用复合类本身上的比较方法,例如__lt__()
或__eq__()
方法。由此可见,上面的Point
dataclass 也不需要为上述 SQL 操作实现 dataclassorder=True
参数。 重新定义复合类型的比较操作 部分包含有关如何自定义比较操作的背景信息。更新 Vertex 实例上的 Point 对象
默认情况下,
Point
对象必须被新对象替换才能检测到更改>>> v1 = session.scalars(select(Vertex)).one()
SELECT vertices.id, vertices.x1, vertices.y1, vertices.x2, vertices.y2 FROM vertices [...] ()>>> v1.end = Point(x=10, y=14) >>> session.commit()UPDATE vertices SET x2=?, y2=? WHERE vertices.id = ? [...] (10, 14, 1) COMMIT为了允许对复合对象进行就地更改,必须使用 Mutation Tracking 扩展。 有关示例,请参阅 在复合类型上建立可变性 部分。
复合类型的其他映射形式¶
composite()
构造可以使用 mapped_column()
构造、Column
或现有映射列的字符串名称传递相关列。以下示例说明了与上面主要部分等效的映射。
直接映射列,然后传递给 composite¶
在这里,我们将现有的 mapped_column()
实例传递给 composite()
构造,如下面的非注解示例所示,其中我们还将 Point
类作为第一个参数传递给 composite()
from sqlalchemy import Integer
from sqlalchemy.orm import mapped_column, composite
class Vertex(Base):
__tablename__ = "vertices"
id = mapped_column(Integer, primary_key=True)
x1 = mapped_column(Integer)
y1 = mapped_column(Integer)
x2 = mapped_column(Integer)
y2 = mapped_column(Integer)
start = composite(Point, x1, y1)
end = composite(Point, x2, y2)
直接映射列,将属性名称传递给 composite¶
我们可以使用更多注解形式编写上面的相同示例,其中我们可以选择将属性名称传递给 composite()
而不是完整的列构造
from sqlalchemy.orm import mapped_column, composite, Mapped
class Vertex(Base):
__tablename__ = "vertices"
id: Mapped[int] = mapped_column(primary_key=True)
x1: Mapped[int]
y1: Mapped[int]
x2: Mapped[int]
y2: Mapped[int]
start: Mapped[Point] = composite("x1", "y1")
end: Mapped[Point] = composite("x2", "y2")
命令式映射和命令式表¶
当使用 命令式表 或完全 命令式 映射时,我们可以直接访问 Column
对象。这些对象也可以传递给 composite()
,如下面的命令式示例所示
mapper_registry.map_imperatively(
Vertex,
vertices_table,
properties={
"start": composite(Point, vertices_table.c.x1, vertices_table.c.y1),
"end": composite(Point, vertices_table.c.x2, vertices_table.c.y2),
},
)
使用旧式的非数据类¶
如果不使用 dataclass,则自定义数据类型类的要求是它有一个构造函数,该构造函数接受与其列格式对应的位置参数,并且还提供一个方法 __composite_values__()
,该方法以列表或元组的形式返回对象的状态,顺序与其基于列的属性的顺序相同。它还应提供足够的 __eq__()
和 __ne__()
方法,以测试两个实例的相等性。
为了说明主要部分中不使用 dataclass 的等效 Point
类
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __composite_values__(self):
return self.x, self.y
def __repr__(self):
return f"Point(x={self.x!r}, y={self.y!r})"
def __eq__(self, other):
return isinstance(other, Point) and other.x == self.x and other.y == self.y
def __ne__(self, other):
return not self.__eq__(other)
然后继续与 composite()
一起使用,其中要与 Point
类关联的列也必须使用显式类型声明,使用 复合类型的其他映射形式 中的一种形式。
跟踪复合类型的就地修改¶
对现有复合值的就地更改不会自动跟踪。相反,复合类需要显式地向其父对象提供事件。此任务主要通过使用 MutableComposite
mixin 自动完成,该 mixin 使用事件将每个用户定义的复合对象与所有父关联关联。请参阅 在复合类型上建立可变性 中的示例。
重新定义复合类型的比较操作¶
默认情况下,“等于”比较操作会生成所有相应列相互等同的 AND。可以使用 composite()
的 comparator_factory
参数来更改此行为,我们可以在其中指定自定义 Comparator
类来定义现有或新操作。下面我们说明“大于”运算符,实现与基本“大于”相同的表达式
import dataclasses
from sqlalchemy.orm import composite
from sqlalchemy.orm import CompositeProperty
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.sql import and_
@dataclasses.dataclass
class Point:
x: int
y: int
class PointComparator(CompositeProperty.Comparator):
def __gt__(self, other):
"""redefine the 'greater than' operation"""
return and_(
*[
a > b
for a, b in zip(
self.__clause_element__().clauses,
dataclasses.astuple(other),
)
]
)
class Base(DeclarativeBase):
pass
class Vertex(Base):
__tablename__ = "vertices"
id: Mapped[int] = mapped_column(primary_key=True)
start: Mapped[Point] = composite(
mapped_column("x1"), mapped_column("y1"), comparator_factory=PointComparator
)
end: Mapped[Point] = composite(
mapped_column("x2"), mapped_column("y2"), comparator_factory=PointComparator
)
由于 Point
是一个 dataclass,我们可以使用 dataclasses.astuple()
来获取 Point
实例的元组形式。
然后,自定义比较器返回适当的 SQL 表达式
>>> print(Vertex.start > Point(5, 6))
vertices.x1 > :x1_1 AND vertices.y1 > :y1_1
嵌套复合类型¶
可以定义复合对象以在简单的嵌套方案中工作,方法是在复合类中重新定义行为以按需工作,然后正常地将复合类映射到完整长度的各个列。这需要定义其他方法来在“嵌套”和“扁平”形式之间移动。
下面我们重新组织 Vertex
类,使其本身成为引用 Point
对象的复合对象。Vertex
和 Point
可以是 dataclass,但是我们将向 Vertex
添加一个自定义构造方法,该方法可以用于创建新的 Vertex
对象,给定四个列值,我们将任意命名为 _generate()
并定义为 classmethod,以便我们可以通过将值传递给 Vertex._generate()
方法来创建新的 Vertex
对象。
我们还将实现 __composite_values__()
方法,这是一个 composite()
构造识别的固定名称(之前在 使用旧式的非数据类 中介绍过),它指示接收对象作为列值的扁平元组的标准方法,在这种情况下,它将取代通常的面向 dataclass 的方法。
使用我们的自定义 _generate()
构造函数和 __composite_values__()
序列化器方法,我们现在可以在列的扁平元组和包含 Point
实例的 Vertex
对象之间移动。Vertex._generate
方法作为第一个参数传递给 composite()
构造,作为新 Vertex
实例的来源,__composite_values__()
方法将由 composite()
隐式使用。
为了示例的目的,Vertex
复合类型然后被映射到一个名为 HasVertex
的类,这是包含四个源列的 Table
最终驻留的位置
from __future__ import annotations
import dataclasses
from typing import Any
from typing import Tuple
from sqlalchemy.orm import composite
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
@dataclasses.dataclass
class Point:
x: int
y: int
@dataclasses.dataclass
class Vertex:
start: Point
end: Point
@classmethod
def _generate(cls, x1: int, y1: int, x2: int, y2: int) -> Vertex:
"""generate a Vertex from a row"""
return Vertex(Point(x1, y1), Point(x2, y2))
def __composite_values__(self) -> Tuple[Any, ...]:
"""generate a row from a Vertex"""
return dataclasses.astuple(self.start) + dataclasses.astuple(self.end)
class Base(DeclarativeBase):
pass
class HasVertex(Base):
__tablename__ = "has_vertex"
id: Mapped[int] = mapped_column(primary_key=True)
x1: Mapped[int]
y1: Mapped[int]
x2: Mapped[int]
y2: Mapped[int]
vertex: Mapped[Vertex] = composite(Vertex._generate, "x1", "y1", "x2", "y2")
上面的映射然后可以在 HasVertex
、Vertex
和 Point
方面使用
hv = HasVertex(vertex=Vertex(Point(1, 2), Point(3, 4)))
session.add(hv)
session.commit()
stmt = select(HasVertex).where(HasVertex.vertex == Vertex(Point(1, 2), Point(3, 4)))
hv = session.scalars(stmt).first()
print(hv.vertex.start)
print(hv.vertex.end)
Composite API¶
对象名称 | 描述 |
---|---|
composite([_class_or_attr], *attrs, [group, deferred, raiseload, comparator_factory, active_history, init, repr, default, default_factory, compare, kw_only, hash, info, doc], **__kw) |
返回用于 Mapper 的基于复合列的属性。 |
- function sqlalchemy.orm.composite(_class_or_attr: None | Type[_CC] | Callable[..., _CC] | _CompositeAttrType[Any] = None, *attrs: _CompositeAttrType[Any], group: str | None = None, deferred: bool = False, raiseload: bool = False, comparator_factory: Type[Composite.Comparator[_T]] | None = None, active_history: bool = False, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: Any | None = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, info: _InfoType | None = None, doc: str | None = None, **__kw: Any) → Composite[Any]¶
返回用于 Mapper 的基于复合列的属性。
有关完整用法示例,请参见映射文档部分 复合列类型。
composite()
返回的MapperProperty
是Composite
。- 参数:
class_¶ – “复合类型”类,或任何类方法或可调用对象,它们将按顺序根据列值生成复合对象的新实例。
*attrs¶ –
要映射的元素列表,可以包括
Column
对象映射类上其他属性的字符串名称,可以是任何其他 SQL 或对象映射属性。例如,这可以允许引用多对一关系的复合类型
active_history=False¶ – 当为
True
时,表示应在替换标量属性时加载“previous”值(如果尚未加载)。请参阅column_property()
上的相同标志。group¶ – 当标记为 deferred 时,此属性的组名称。
deferred¶ – 当为 True 时,列属性是“deferred”的,这意味着它不会立即加载,而是在首次访问实例上的属性时加载。另请参见
deferred()
。comparator_factory¶ – 一个类,它扩展了
Comparator
,为比较操作提供自定义 SQL 子句生成。doc¶ – 可选字符串,将作为类绑定的描述符上的文档应用。
info¶ – 可选数据字典,将填充到此对象的
MapperProperty.info
属性中。default_factory¶ – 专用于 声明式数据类映射,指定一个默认值生成函数,该函数将作为数据类进程生成的
__init__()
方法的一部分执行。compare¶ –
专用于 声明式数据类映射,指示在为映射类生成
__eq__()
和__ne__()
方法时,此字段是否应包含在比较操作中。2.0.0b4 版本新增。
hash¶ –
专用于 声明式数据类映射,控制在为映射类生成
__hash__()
方法时,是否包含此字段。2.0.36 版本新增。
flambé! 龙和 The Alchemist 图像设计由 Rotem Yaari 创建并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档上次生成时间:Tue 11 Mar 2025 02:40:17 PM EDT