SqlAlchemy 2.0 中文文档(八)(4)https://developer.aliyun.com/article/1559851
复合对象的其他映射形式
composite()
构造可以使用 mapped_column()
构造、Column
或现有映射列的字符串名称来传递相关列。以下示例说明了与上述主要部分相同的等效映射。
直接映射列,然后传递给复合对象
在这里,我们将现有的 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()
而不是完整的列构造:
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), }, )
直接映射列,然后传递给复合对象
在这里,我们将现有的 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()
而不是完整的列构造:
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), }, )
使用传统非数据类
如果不使用数据类,则自定义数据类型类的要求是它具有一个构造函数,该构造函数接受与其列格式对应的位置参数,并且还提供一个__composite_values__()
方法,该方法按照其基于列的属性的顺序返回对象的状态列表或元组。它还应该提供足够的__eq__()
和__ne__()
方法来测试两个实例的相等性。
为了说明主要部分中的等效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,这项任务主要通过使用事件将每个用户定义的复合对象与所有父关联关联起来来自动完成。请参阅为复合对象建立可变性中的示例。
重新定义复合对象的比较操作
默认情况下,“equals”比较操作会产生所有对应列等于彼此的 AND。可以使用composite()
的comparator_factory
参数进行更改,其中我们指定一个自定义的Comparator
类来定义现有或新的操作。下面我们说明“greater than”运算符,实现与基本“greater than”相同的表达式:
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
是一个数据类,我们可以利用dataclasses.astuple()
来获得Point
实例的元组形式。
然后,自定义比较器返回适当的 SQL 表达式:
>>> print(Vertex.start > Point(5, 6)) vertices.x1 > :x1_1 AND vertices.y1 > :y1_1
嵌套复合对象
可以定义复合对象以在简单的嵌套方案中工作,方法是在复合类中重新定义所需的行为,然后将复合类映射到通常的单个列的完整长度。这要求定义额外的方法来在“嵌套”和“扁平”形式之间移动。
接下来,我们重新组织Vertex
类本身成为一个引用Point
对象的复合对象。 Vertex
和Point
可以是数据类,但是我们将向Vertex
添加一个自定义构造方法,该方法可用于根据四个列值创建新的Vertex
对象,我们将任意命名为_generate()
并定义为类方法,以便我们可以通过将值传递给Vertex._generate()
方法来创建新的Vertex
对象。
我们还将实现__composite_values__()
方法,这是由composite()
构造(在使用传统非数据类中介绍)中识别的固定名称,表示以列值的扁平元组形式接收对象的标准方式,在这种情况下将取代通常的数据类导向方法。
通过我们的自定义_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)
复合 API
对象名称 | 描述 |
composite([_class_or_attr], *attrs, [group, deferred, raiseload, comparator_factory, active_history, init, repr, default, default_factory, compare, kw_only, 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, info: _InfoType | None = None, doc: str | None = None, **__kw: Any) → Composite[Any]
返回用于与 Mapper 一起使用的基于复合列的属性。
查看映射文档部分复合列类型以获取完整的使用示例。
composite()
返回的MapperProperty
是Composite
。
参数:
class_
– “复合类型”类,或任何类方法或可调用对象,将根据顺序的列值生成复合对象的新实例。*attrs
–要映射的元素列表,可能包括:
Column
对象mapped_column()
构造- 映射类上的其他属性的字符串名称,这些属性可以是任何其他 SQL 或对象映射的属性。例如,这可以允许一个复合属性引用到一个多对一的关系。
active_history=False
– 当为True
时,指示在替换时应加载标量属性的“先前”值,如果尚未加载。请参阅column_property()
上的相同标志。group
– 标记为延迟加载时,此属性的组名。deferred
– 当为 True 时,列属性为“延迟加载”,意味着它不会立即加载,而是在首次访问实例上的属性时加载。另请参阅deferred()
。comparator_factory
– 一个扩展Comparator
的类,提供自定义的 SQL 子句生成以进行比较操作。doc
– 可选字符串,将应用为类绑定描述符的文档。info
– 可选的数据字典,将填充到此对象的MapperProperty.info
属性中。init
– 特定于声明式数据类映射,指定映射属性是否应作为数据类处理生成的__init__()
方法的一部分。repr
– 特定于声明式数据类映射,指定映射属性是否应作为数据类处理生成的__repr__()
方法的一部分。default_factory
– 特定于声明式数据类映射,指定将作为数据类处理生成的__init__()
方法的一部分而发生的默认值生成函数。compare
–
特定于声明式数据类映射,指示在为映射类生成__eq__()
和__ne__()
方法时,此字段是否应包含在比较操作中。
新功能在版本 2.0.0b4 中引入。kw_only
– 特定于声明式数据类映射,指示在生成__init__()
时此字段是否应标记为关键字参数。
== Vertex(Point(1, 2), Point(3, 4)))
hv = session.scalars(stmt).first()
print(hv.vertex.start)
print(hv.vertex.end)
## 复合 API | 对象名称 | 描述 | | --- | --- | | composite([_class_or_attr], *attrs, [group, deferred, raiseload, comparator_factory, active_history, init, repr, default, default_factory, compare, kw_only, info, doc], **__kw) | 返回用于与 Mapper 一起使用的基于复合列的属性。 | ```py 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, info: _InfoType | None = None, doc: str | None = None, **__kw: Any) → Composite[Any]
返回用于与 Mapper 一起使用的基于复合列的属性。
查看映射文档部分复合列类型以获取完整的使用示例。
composite()
返回的MapperProperty
是Composite
。
参数:
class_
– “复合类型”类,或任何类方法或可调用对象,将根据顺序的列值生成复合对象的新实例。*attrs
–要映射的元素列表,可能包括:
Column
对象mapped_column()
构造- 映射类上的其他属性的字符串名称,这些属性可以是任何其他 SQL 或对象映射的属性。例如,这可以允许一个复合属性引用到一个多对一的关系。
active_history=False
– 当为True
时,指示在替换时应加载标量属性的“先前”值,如果尚未加载。请参阅column_property()
上的相同标志。group
– 标记为延迟加载时,此属性的组名。deferred
– 当为 True 时,列属性为“延迟加载”,意味着它不会立即加载,而是在首次访问实例上的属性时加载。另请参阅deferred()
。comparator_factory
– 一个扩展Comparator
的类,提供自定义的 SQL 子句生成以进行比较操作。doc
– 可选字符串,将应用为类绑定描述符的文档。info
– 可选的数据字典,将填充到此对象的MapperProperty.info
属性中。init
– 特定于声明式数据类映射,指定映射属性是否应作为数据类处理生成的__init__()
方法的一部分。repr
– 特定于声明式数据类映射,指定映射属性是否应作为数据类处理生成的__repr__()
方法的一部分。default_factory
– 特定于声明式数据类映射,指定将作为数据类处理生成的__init__()
方法的一部分而发生的默认值生成函数。compare
–
特定于声明式数据类映射,指示在为映射类生成__eq__()
和__ne__()
方法时,此字段是否应包含在比较操作中。
新功能在版本 2.0.0b4 中引入。kw_only
– 特定于声明式数据类映射,指示在生成__init__()
时此字段是否应标记为关键字参数。