SqlAlchemy-2-0-中文文档-五-

SqlAlchemy 2.0 中文文档(五)

原文:docs.sqlalchemy.org/en/20/contents.html

复合列类型

原文:docs.sqlalchemy.org/en/20/orm/composites.html

列集合可以关联到一个单一用户定义的数据类型,现代使用中通常是一个 Python dataclass。ORM 提供了一个属性,使用您提供的类来表示列的组。

一个简单的例子表示一对 Integer 列作为一个 Point 对象,带有属性 .x.y。使用 dataclass,这些属性使用相应的 int Python 类型定义:

import dataclasses

@dataclasses.dataclass
class Point:
    x: int
    y: int

也接受非 dataclass 形式,但需要实现额外的方法。有关使用非 dataclass 类的示例,请参见 Using Legacy Non-Dataclasses 部分。

2.0 版中新增:composite() 构造完全支持 Python dataclasses,包括从复合类派生映射列数据类型的能力。

我们将创建一个映射到表 vertices 的映射,表示两个点为 x1/y1x2/y2。使用 composite() 构造将 Point 类与映射列关联起来。

下面的示例说明了与完全 Annotated Declarative Table 配置一起使用的最现代形式的 composite()mapped_column() 构造表示每个列直接传递给 composite(),指示要生成的列的零个或多个方面,在这种情况下是名称;composite() 构造直接从数据类中推导列类型(在本例中为 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})"

提示

在上面的示例中,表示复合的列(x1y1 等)也可以在类上访问,但类型检查器不能正确理解。如果访问单列很重要,可以明确声明它们,如 Map columns directly, pass attribute names to 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 类的实例。 x1y1x2y2 列被透明处理:

  • 持久化 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.startVertex.end 属性在使用 ORM Session(包括传统的 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.startVertex.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__() 方法。 由此可见,上述 SQL 操作也不需要实现数据类 order=True 参数。重新定义复合操作部分包含如何自定义比较操作的背景信息。

  • 更新顶点实例上的 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 或现有映射列的字符串名称来传递相关列。以下示例说明了与上述主要部分相同的等效映射。

直接映射列,然后传递给复合对象

在这里,我们将现有的 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` 类不使用数据类:

```py
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 混合类,大部分工作已自动化,该类使用事件将每个用户定义的组合对象与所有父关联关联起来。请参阅在组合类型上建立可变性中的示例。

重新定义组合类型的比较操作

默认情况下,“equals”比较操作产生所有相应列相等的 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是一个数据类,我们可以使用dataclasses.astuple()来获得Point实例的元组形式。

然后,自定义比较器返回适当的 SQL 表达式:

>>> print(Vertex.start > Point(5, 6))
vertices.x1  >  :x1_1  AND  vertices.y1  >  :y1_1 

嵌套复合体

复合对象可以被定义为在简单的嵌套方案中工作,通过在复合类内重新定义所需的行为,然后将复合类映射到通常的各列的全长。这需要定义额外的方法来在“嵌套”和“平面”形式之间移动。

下面我们重新组织Vertex类,使其本身成为一个复合对象,引用Point对象。VertexPoint可以是数据类,但是我们将在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")

然后,上述映射可以根据HasVertexVertexPoint来使用:

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() 返回的 MapperPropertyComposite

参数:

  • 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 – 特定于 声明性 Dataclass 映射,指定将作为 __init__() 方法的一部分执行的默认值生成函数。

  • compare

    特定于 声明性 Dataclass 映射,指示在生成映射类的 __eq__()__ne__() 方法时是否应包括此字段的比较操作。

    新版本 2.0.0b4 中。

  • kw_only – 特定于 声明性 Dataclass 映射,指示是否在生成 __init__() 时将此字段标记为关键字参数。

使用映射复合列类型

如上一节所示的映射,我们可以使用 Vertex 类,其中 .start.end 属性将透明地引用 Point 类引用的列,以及使用 Vertex 类的实例,其中 .start.end 属性将引用 Point 类的实例。x1y1x2y2 列将被透明处理:

  • 持久化 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.startVertex.end 属性在使用 ORM Session(包括传统的 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.startVertex.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 数据类也无需实现 dataclasses 的 order=True 参数,上述 SQL 操作就可以正常工作。复合操作重新定义比较操作 包含了如何定制比较操作的背景信息。

  • 在 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 或现有映射列的字符串名称来传递相关列。以下示例说明了与上述主要部分相同的等效映射。

直接映射列,然后传递给复合对象

在这里,我们将现有的 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对象的复合对象。 VertexPoint可以是数据类,但是我们将向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")

上述映射可以根据HasVertexVertexPoint来使用:

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()返回的MapperPropertyComposite

参数:

  • 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__()时此字段是否应标记为关键字参数。

映射类继承层次结构

原文:docs.sqlalchemy.org/en/20/orm/inheritance.html

SQLAlchemy 支持三种继承形式:

  • 单表继承 – 几种类别的类别由单个表表示;

  • 具体表继承 – 每种类别的类别都由独立的表表示;

  • 联接表继承 – 类层次结构在依赖表之间分解。每个类由其自己的表表示,该表仅包含该类本地的属性。

最常见的继承形式是单一和联接表,而具体继承则提出了更多的配置挑战。

当映射器配置在继承关系中时,SQLAlchemy 有能力以多态方式加载元素,这意味着单个查询可以返回多种类型的对象。

另请参见

为继承映射编写 SELECT 语句 - 在 ORM 查询指南 中

继承映射示例 - 联接、单一和具体继承的完整示例

联接表继承

在联接表继承中,沿着类层次结构的每个类都由一个不同的表表示。对类层次结构中特定子类的查询将作为 SQL JOIN 在其继承路径上的所有表之间进行。如果查询的类是基类,则查询基表,同时可以选择包含其他表或允许后续加载特定于子表的属性的选项。

在所有情况下,对于给定行要实例化的最终类由基类上定义的鉴别器列或 SQL 表达式确定,该列将生成与特定子类关联的标量值。

联接继承层次结构中的基类将配置具有指示多态鉴别器列以及可选地为基类本身配置的多态标识符的其他参数:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

class Base(DeclarativeBase):
    pass

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

    def __repr__(self):
        return f"{self.__class__.__name__}({self.name!r})"

在上面的示例中,鉴别器是 type 列,可以使用 Mapper.polymorphic_on 参数进行配置。该参数接受一个面向列的表达式,可以指定为要使用的映射属性的字符串名称,也可以指定为列表达式对象,如 Columnmapped_column() 构造。

鉴别器列将存储指示行内表示的对象类型的值。该列可以是任何数据类型,但字符串和整数是最常见的。要为数据库中的特定行应用到该列的实际数据值是使用下面描述的 Mapper.polymorphic_identity 参数指定的。

尽管多态鉴别器表达式不是严格必需的,但如果需要多态加载,则需要它。在基础表上建立列是实现这一点的最简单方法,然而非常复杂的继承映射可能会使用 SQL 表达式,例如 CASE 表达式,作为多态鉴别器。

注意

目前,整个继承层次结构只能配置一个鉴别器列或 SQL 表达式,通常在层次结构中最基本的类上。暂时不支持“级联”多态鉴别器表达式。

我们接下来定义 EngineerManagerEmployee 子类。每个类包含代表其所代表的子类的唯一属性的列。每个表还必须包含一个主键列(或列),以及对父表的外键引用:

class Engineer(Employee):
    __tablename__ = "engineer"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    engineer_name: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

在上面的示例中,每个映射都在其映射器参数中指定了Mapper.polymorphic_identity参数。此值填充了基础映射器上建立的Mapper.polymorphic_on参数指定的列。每个映射类的Mapper.polymorphic_identity参数应在整个层次结构中是唯一的,并且每个映射类应只有一个“标识”;如上所述,“级联”标识不支持一些子类引入第二个标识的情况。

ORM 使用由Mapper.polymorphic_identity设置的值来确定加载行的多态时行属于哪个类。在上面的示例中,每个代表Employee的行将在其type列中具有值'employee';类似地,每个Engineer将获得值'engineer',每个Manager将获得值'manager'。无论继承映射是否为子类使用不同的连接表(如连接表继承)或所有一个表(如单表继承),这个值都应该被持久化并在查询时对 ORM 可用。Mapper.polymorphic_identity参数也适用于具体表继承,但实际上并没有持久化;有关详细信息,请参阅后面的具体表继承部分。

在多态设置中,最常见的是外键约束建立在与主键本身相同的列或列上,但这并非必需;也可以使与主键不同的列引用到父级的外键。从基表到子类的 JOIN 的构建方式也是可直接自定义的,但这很少是必要的。

使用连接继承映射完成后,针对Employee的查询将返回EmployeeEngineerManager对象的组合。新保存的EngineerManagerEmployee对象在这种情况下将自动填充employee.type列中的正确“识别器”值,如"engineer""manager""employee"

使用连接继承的关系

使用连接表继承完全支持关系。涉及连接继承类的关系应该针对在层次结构中也对应于外键约束的类;在下面的示例中,由于employee表有一个回到company表的外键约束,因此关系被设置在CompanyEmployee之间:

from __future__ import annotations

from sqlalchemy.orm import relationship

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee): ...

class Engineer(Employee): ...

如果外键约束在对应于子类的表上,关系应该指向该子类。在下面的示例中,有一个从managercompany的外键约束,因此关系建立在ManagerCompany类之间:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee): ...

在上面的例子中,Manager类将有一个Manager.company属性;Company将有一个Company.managers属性,总是对employeemanager表一起加载。

加载连接继承映射

请参阅编写继承映射的 SELECT 语句部分,了解关于继承加载技术的背景,包括在映射器配置时间和查询时间配置要查询的表。## 单表继承

单表继承在单个表中表示所有子类的所有属性。具有唯一于该类的属性的特定子类将在表中的列中保留它们,如果行引用了不同类型的对象,则这些列将为空。

在层次结构中查询特定子类将呈现为针对基表的 SELECT 查询,其中将包括一个 WHERE 子句,该子句限制行为具有鉴别器列或表达式中存在的特定值或值的行。

单表继承相对于联接表继承具有简单性的优势;查询要高效得多,因为只需要涉及一个表来加载每个表示类的对象。

单表继承配置看起来很像联接表继承,除了只有基类指定了__tablename__。还需要在基表上有一个鉴别器列,以便类可以彼此区分。

即使子类共享所有属性的基表,在使用声明性时,仍然可以在子类上指定mapped_column对象,指示该列仅映射到该子类;mapped_column将应用于相同的基本Table对象:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Manager(Employee):
    manager_data: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

请注意,派生类 Manager 和 Engineer 的映射器省略了__tablename__,这表明它们没有自己的映射表。另外,包含了一个带有nullable=Truemapped_column()指令;由于为这些类声明的 Python 类型不包括Optional[],因此该列通常被映射为NOT NULL,这对于该列只期望被填充为那些对应于该特定子类的行并不合适。

使用use_existing_column解决列冲突

请注意,在上一节中,manager_nameengineer_info列被“上移”以应用于Employee.__table__,因为它们在没有自己的表的子类上声明。当两个子类想要指定相同的列时,就会出现一个棘手的情况,如下所示:

from datetime import datetime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

上面,在EngineerManager上声明的start_date列将导致错误:

sqlalchemy.exc.ArgumentError: Column 'start_date' on class Manager conflicts
with existing column 'employee.start_date'.  If using Declarative,
consider using the use_existing_column parameter of mapped_column() to
resolve conflicts.

上述场景对声明式映射系统存在一种模糊性,可以通过在mapped_column()上使用mapped_column.use_existing_column参数来解决,该参数指示mapped_column()在存在继承的超类时查找并使用已经映射的列,如果已经存在,则映射一个新列:

from sqlalchemy import DateTime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

上面的例子中,当 Manager 被映射时,start_date 列已经存在于 Employee 类中,已经由 Engineer 映射提供。mapped_column.use_existing_column参数指示 mapped_column() 应首先在 Employee 的映射 Table 中查找请求的 Column,如果存在,则保持该现有映射。如果不存在,mapped_column()将正常映射列,将其添加为由 Employee 超类引用的 Table 中的列之一。

版本 2.0.0b4 中新增:- 添加了mapped_column.use_existing_column,提供了一种在继承子类上有条件地映射列的 2.0 兼容方法。之前的方法结合了 declared_attr 和对父类 .__table__ 的查找,仍然可以正常工作,但缺乏PEP 484的类型支持。

可以使用类似的概念来定义特定系列的列和/或其他可重复使用的混合类中的映射属性(请参阅使用混合类组合映射层次结构):

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": type,
        "polymorphic_identity": "employee",
    }

class HasStartDate:
    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Engineer(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

class Manager(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

单表继承关系

关系完全支持单表继承。配置方式与连接继承的方式相同;外键属性应该在关系的“外键”一侧的同一类上:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_data: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

同样,类似于连接继承的情况,我们可以创建涉及特定子类的关系。在查询时,SELECT 语句将包括一个 WHERE 子句,将类选择限制为该子类或子类:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_name: Mapped[str] = mapped_column(nullable=True)

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

在上面的例子中,Manager类将具有Manager.company属性;Company将具有一个Company.managers属性,该属性始终针对employee加载,并附加一个 WHERE 子句,限制行为具有type = 'manager'的行。

使用polymorphic_abstract构建更深层次的层次结构

在 2.0 版本中新增。

在构建任何类型的继承层次结构时,映射类可以包括设置为TrueMapper.polymorphic_abstract参数,这表明该类应该被正常映射,但不希望直接实例化,并且不包括Mapper.polymorphic_identity。然后可以声明这个映射类的子类,这些子类本身可以包括一个Mapper.polymorphic_identity,因此可以正常使用。这允许一系列子类被一个通用的基类引用,该基类在层次结构中被视为“抽象”,在查询和relationship()声明中都是如此。这种用法与在 Declarative 中使用 abstract 属性不同,后者将目标类完全取消映射,因此无法单独作为映射类使用。Mapper.polymorphic_abstract可以应用于层次结构中的任何类或类,包括同时应用于多个级别的类。

例如,假设ManagerPrincipal都被分类到一个超类Executive,而EngineerSysadmin被分类到一个超类TechnologistExecutiveTechnologist都不会被实例化,因此没有Mapper.polymorphic_identity。可以使用Mapper.polymorphic_abstract来配置这些类,如下所示:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Executive(Employee):
  """An executive of the company"""

    executive_background: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Technologist(Employee):
  """An employee who works with technology"""

    competencies: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Manager(Executive):
  """a manager"""

    __mapper_args__ = {"polymorphic_identity": "manager"}

class Principal(Executive):
  """a principal of the company"""

    __mapper_args__ = {"polymorphic_identity": "principal"}

class Engineer(Technologist):
  """an engineer"""

    __mapper_args__ = {"polymorphic_identity": "engineer"}

class SysAdmin(Technologist):
  """a systems administrator"""

    __mapper_args__ = {"polymorphic_identity": "sysadmin"}

在上面的示例中,新的类 TechnologistExecutive 都是普通的映射类,并且还指示要添加到超类的新列 executive_backgroundcompetencies。然而,它们都缺少对 Mapper.polymorphic_identity 的设置;这是因为不希望直接实例化 TechnologistExecutive;我们总是会有 ManagerPrincipalEngineerSysAdmin 中的一个。然而,我们可以查询 PrincipalTechnologist 角色,并且让它们成为relationship()的目标。下面的示例演示了对 Technologist 对象的 SELECT 语句:

session.scalars(select(Technologist)).all()
SELECT  employee.id,  employee.name,  employee.type,  employee.competencies
FROM  employee
WHERE  employee.type  IN  (?,  ?)
[...]  ('engineer',  'sysadmin') 

TechnologistExecutive 的抽象映射类也可以作为relationship()映射的目标,就像任何其他映射类一样。我们可以扩展上面的示例,包括 Company,其中包含单独的集合 Company.technologistsCompany.principals

class Company(Base):
    __tablename__ = "company"
    id = Column(Integer, primary_key=True)

    executives: Mapped[List[Executive]] = relationship()
    technologists: Mapped[List[Technologist]] = relationship()

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)

    # foreign key to "company.id" is added
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))

    # rest of mapping is the same
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
    }

# Executive, Technologist, Manager, Principal, Engineer, SysAdmin
# classes from previous example would follow here unchanged

使用上述映射,我们可以分别跨 Company.technologistsCompany.executives 使用联接和关系加载技术:

session.scalars(
    select(Company)
    .join(Company.technologists)
    .where(Technologist.competency.ilike("%java%"))
    .options(selectinload(Company.executives))
).all()
SELECT  company.id
FROM  company  JOIN  employee  ON  company.id  =  employee.company_id  AND  employee.type  IN  (?,  ?)
WHERE  lower(employee.competencies)  LIKE  lower(?)
[...]  ('engineer',  'sysadmin',  '%java%')

SELECT  employee.company_id  AS  employee_company_id,  employee.id  AS  employee_id,
employee.name  AS  employee_name,  employee.type  AS  employee_type,
employee.executive_background  AS  employee_executive_background
FROM  employee
WHERE  employee.company_id  IN  (?)  AND  employee.type  IN  (?,  ?)
[...]  (1,  'manager',  'principal') 

另请参阅

abstract - 声明参数,允许在继承层次结构中完全取消映射声明的类,同时仍然继承自映射的超类。

加载单表继承映射

单表继承的加载技术与连接表继承的加载技术大部分相同,并且提供了这两种映射类型之间的高度抽象,因此很容易在它们之间切换,以及在单个继承层次结构中混合使用它们(只需从要单继承的子类中省略 __tablename__)。请参阅编写用于继承映射的 SELECT 语句和单表继承映射的 SELECT 语句章节,了解有关继承加载技术的文档,包括在映射器配置时间和查询时间配置要查询的类。## 具体表继承

具体继承将每个子类映射到其自己的独立表,每个表包含产生该类实例所需的所有列。具体继承配置默认以非多态方式查询;对于特定类的查询将仅查询该类的表,并且仅返回该类的实例。通过在映射器内配置特殊的 SELECT,通常会将所有表的 UNION 作为结果来启用具体类的多态加载。

警告

具体表继承比连接或单表继承复杂得多,在使用关系、急加载和多态加载方面功能受限,尤其是与其一起使用时。当以多态方式使用时,会生成非常大的查询,其中包含不会像简单连接那样执行得好的 UNION。强烈建议如果需要关系加载和多态加载的灵活性,尽量使用连接或单表继承。如果不需要多态加载,则每个类完全引用自己的表时可以使用普通的非继承映射。

相比于连接和单表继承在“多态”加载方面更为流畅,具体继承在这方面更为麻烦。因此,当不需要多态加载时,具体继承更为合适。建立涉及具体继承类的关系也更为麻烦。

要将类标记为使用具体继承,需要在__mapper_args__中添加Mapper.concrete参数。这表示对于声明式和映射来说,超类表不应被视为映射的一部分:

class Employee(Base):
    __tablename__ = "employee"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))

class Manager(Employee):
    __tablename__ = "manager"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(50))

    __mapper_args__ = {
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(50))

    __mapper_args__ = {
        "concrete": True,
    }

应该注意两个关键点:

  • 我们必须在每个子类上明确定义所有列,即使是同名的列也是如此。例如此处的Employee.name不会被复制到ManagerEngineer映射的表中。

  • 虽然EngineerManager类与Employee之间有映射关系,但它们不包括多态加载。这意味着,如果我们查询Employee对象,managerengineer表根本不会被查询。

具体多态加载配置

具体继承的多态加载要求针对应具有多态加载的每个基类配置一个专门的 SELECT。此 SELECT 需要能够单独访问所有映射的表,并且通常是使用 SQLAlchemy 助手polymorphic_union()构造的 UNION 语句。

如为继承映射编写 SELECT 语句中所讨论的,任何类型的映射继承配置都可以配置为默认从特殊可选中加载,使用Mapper.with_polymorphic参数。当前的公共 API 要求在首次构造Mapper时设置此参数。

但是,在使用 Declarative 的情况下,映射器和被映射的Table同时创建,一旦定义了映射的类。这意味着由于尚未定义对应于子类的Table对象,因此暂时无法提供Mapper.with_polymorphic参数。

有几种可用的策略来解决这个循环,但是 Declarative 提供了处理此问题的助手类ConcreteBaseAbstractConcreteBase

使用ConcreteBase,我们可以几乎以与其他形式的继承映射相同的方式设置我们的具体映射:

from sqlalchemy.ext.declarative import ConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

上面,Declarative 在映射器“初始化”时为Employee类设置了多态可选项;这是解决其他依赖映射器的延迟配置步骤。ConcreteBase助手使用polymorphic_union()函数在设置了所有其他类后创建所有具体映射表的 UNION,并然后使用已经存在的基类映射器配置此语句。

在选择时,多态联合会产生这样的查询:

session.scalars(select(Employee)).all()
SELECT
  pjoin.id,
  pjoin.name,
  pjoin.type,
  pjoin.manager_data,
  pjoin.engineer_info
FROM  (
  SELECT
  employee.id  AS  id,
  employee.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'employee'  AS  type
  FROM  employee
  UNION  ALL
  SELECT
  manager.id  AS  id,
  manager.name  AS  name,
  manager.manager_data  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'manager'  AS  type
  FROM  manager
  UNION  ALL
  SELECT
  engineer.id  AS  id,
  engineer.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  engineer.engineer_info  AS  engineer_info,
  'engineer'  AS  type
  FROM  engineer
)  AS  pjoin 

上面的 UNION 查询需要为每个子表制造“NULL”列,以适应那些不是特定子类成员的列。

另请参阅

ConcreteBase ### 抽象具体类

到目前为止,所示的具体映射同时显示了子类和基类分别映射到各自的表中。在具体继承用例中,通常基类在数据库中不会被表示,只有子类。换句话说,基类是“抽象的”。

通常,当想要将两个不同的子类映射到各自的表中,并且将基类保持未映射时,这可以很容易地实现。在使用 Declarative 时,只需使用__abstract__指示符声明基类:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(Base):
    __abstract__ = True

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

上面,我们实际上并没有使用 SQLAlchemy 的继承映射功能;我们可以正常加载和持久化ManagerEngineer的实例。然而,当我们需要多态查询时,情况就会发生变化,也就是说,我们希望发出select(Employee)并返回ManagerEngineer实例的集合。这将我们带回到具体继承的领域,我们必须针对Employee构建一个特殊的映射器才能实现这一点。

要修改我们的具体继承示例,以说明一个能够进行多态加载的“抽象”基类,我们将只有一个engineer和一个manager表,没有employee表,但是Employee映射器将直接映射到“多态联合”,而不是在Mapper.with_polymorphic参数中本地指定它。

为了帮助解决这个问题,Declarative 提供了一种名为AbstractConcreteBaseConcreteBase类的变体,它可以自动实现这一点:

from sqlalchemy.ext.declarative import AbstractConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    name = mapped_column(String(50))

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

Base.registry.configure()

上面,调用了registry.configure()方法,这将触发实际映射Employee类;在配置步骤之前,类没有映射,因为它将从中查询的子表尚未定义。此过程比ConcreteBase更复杂,因为必须延迟基类的整个映射,直到所有子类都已声明。使用像上面的映射,只能持久化ManagerEngineer的实例;对Employee类进行查询将始终产生ManagerEngineer对象。

使用上述映射,可以根据Employee类和在其上本地声明的任何属性生成查询,例如Employee.name

>>> stmt = select(Employee).where(Employee.name == "n1")
>>> print(stmt)
SELECT  pjoin.id,  pjoin.name,  pjoin.type,  pjoin.manager_data,  pjoin.engineer_info
FROM  (
  SELECT  engineer.id  AS  id,  engineer.name  AS  name,  engineer.engineer_info  AS  engineer_info,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,  'engineer'  AS  type
  FROM  engineer
  UNION  ALL
  SELECT  manager.id  AS  id,  manager.name  AS  name,  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  manager.manager_data  AS  manager_data,  'manager'  AS  type
  FROM  manager
)  AS  pjoin
WHERE  pjoin.name  =  :name_1 

AbstractConcreteBase.strict_attrs 参数指示 Employee 类应直接映射仅属于 Employee 类的属性,如本例中的 Employee.name 属性。其他属性,如 Manager.manager_dataEngineer.engineer_info,仅存在于其相应的子类中。当未设置 AbstractConcreteBase.strict_attrs 时,所有子类属性(如 Manager.manager_dataEngineer.engineer_info)都将映射到基类 Employee。这是一种传统的使用模式,可能更方便查询,但其效果是所有子类共享整个层次结构的完整属性集;在上述示例中,不使用 AbstractConcreteBase.strict_attrs 将导致生成非实用的 Engineer.manager_nameManager.engineer_info 属性。

新版 2.0 中:新增了 AbstractConcreteBase.strict_attrs 参数到 AbstractConcreteBase 中,以产生更清晰的映射;默认值为 False,以允许继续使用旧版 1.x 版本中的传统映射。

另请参阅

AbstractConcreteBase

经典和半经典具体多态配置

使用 ConcreteBaseAbstractConcreteBase 说明的声明性配置相当于另外两种使用 polymorphic_union() 显式的配置形式。 这些配置形式明确使用 Table 对象,以便首先创建“多态联合”,然后将其应用于映射。 这些示例旨在澄清 polymorphic_union() 函数在映射中的作用。

例如,半经典映射利用声明性,但分别建立 Table 对象:

metadata_obj = Base.metadata

employees_table = Table(
    "employee",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
)

managers_table = Table(
    "manager",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("manager_data", String(50)),
)

engineers_table = Table(
    "engineer",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("engineer_info", String(50)),
)

接下来,使用 polymorphic_union() 生成 UNION:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "employee": employees_table,
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

使用上述 Table 对象,可以使用“半经典”样式生成映射,在此样式中,我们与 __table__ 参数一起使用声明性;我们上面的多态联合通过 __mapper_args__ 传递给 Mapper.with_polymorphic 参数:

class Employee(Base):
    __table__ = employee_table
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": ("*", pjoin),
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

或者,可以完全以“经典”风格使用相同的 Table 对象,而不使用声明性。 构造函数与声明性提供的类似,如下所示:

class Employee:
    def __init__(self, **kw):
        for k in kw:
            setattr(self, k, kw[k])

class Manager(Employee):
    pass

class Engineer(Employee):
    pass

employee_mapper = mapper_registry.map_imperatively(
    Employee,
    pjoin,
    with_polymorphic=("*", pjoin),
    polymorphic_on=pjoin.c.type,
)
manager_mapper = mapper_registry.map_imperatively(
    Manager,
    managers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="manager",
)
engineer_mapper = mapper_registry.map_imperatively(
    Engineer,
    engineers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="engineer",
)

"抽象" 示例也可以使用“半经典”或“经典”风格进行映射。 不同之处在于,我们不再将“多态联合”应用于 Mapper.with_polymorphic 参数,而是直接将其作为我们最基本的映射器上的映射选择。 半经典映射如下所示:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

class Employee(Base):
    __table__ = pjoin
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": "*",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

在上面的示例中,我们与以前一样使用 polymorphic_union(),只是省略了employee表。

另请参阅

命令式映射 - 有关命令式或“经典”映射的背景信息

具体继承关系

在具体继承场景中,映射关系是具有挑战性的,因为不同的类不共享一个表。如果关系只涉及特定类,例如我们之前示例中的CompanyManager之间的关系,那么不需要特殊步骤,因为这只是两个相关表。

然而,如果Company要与Employee建立一对多关系,表示集合可能包括EngineerManager对象,这意味着Employee必须具有多态加载能力,并且要关联的每个表都必须有一个外键返回到company表。这种配置的示例如下:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

具体继承和关系的下一个复杂性涉及当我们希望EmployeeManagerEngineer中的一个或全部自身引用Company时。对于这种情况,SQLAlchemy 在 Employee 上放置一个与 Company 相关的 relationship() 时,在实例级别执行时不适用于 ManagerEngineer 类,而必须对每个类应用一个不同的 relationship()。为了实现三个独立关系的双向行为,这些关系作为 Company.employees 的相反关系,使用了 relationship.back_populates 参数:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee", back_populates="company")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

上述限制与当前实现相关,包括具体继承类不共享超类的任何属性,因此需要设置不同的关系。

加载具体继承映射

具体继承加载选项有限;通常,如果在映射器上配置了多态加载,使用其中一个声明性具体混合类,就不能在当前 SQLAlchemy 版本中在查询时修改它。通常,with_polymorphic() 函数应该能够覆盖具体使用的加载样式,但由于当前限制,这还不受支持。 ## 连接表继承

在连接表继承中,类层次结构中的每个类都由一个不同的表表示。在层次结构中查询特定子类将作为 SQL JOIN 渲染其继承路径上的所有表。如果查询的类是基类,则将查询基表,同时可以选择包括其他表或允许特定于子表的属性稍后加载。

在所有情况下,给定行的最终实例化类由基类上定义的鉴别器列或 SQL 表达式确定,该列将产生与特定子类关联的标量值。

连接继承层次结构中的基类将配置具有指示多态鉴别器列的额外参数,以及可选的基类自身的多态标识符:

from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column

class Base(DeclarativeBase):
    pass

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

    def __repr__(self):
        return f"{self.__class__.__name__}({self.name!r})"

在上面的例子中,鉴别器是type列,可以使用Mapper.polymorphic_on参数进行配置。该参数接受一个基于列的表达式,可以指定为要使用的映射属性的字符串名称,也可以指定为列表达式对象,如Columnmapped_column()构造。

鉴别器列将存储一个值,该值指示行中表示的对象类型。该列可以是任何数据类型,但字符串和整数最常见。为数据库中的特定行应用于此列的实际数据值是使用Mapper.polymorphic_identity参数指定的,如下所述。

虽然多态鉴别器表达式不是严格必需的,但如果需要多态加载,则需要。在基表上建立一个列是实现此目的的最简单方法,但是非常复杂的继承映射可能会使用 SQL 表达式,例如 CASE 表达式,作为多态鉴别器。

注意

目前,整个继承层次结构仅可以配置一个鉴别器列或 SQL 表达式,通常在层次结构中最基本的类上。目前不支持“级联”多态鉴别器表达式。

我们接下来定义EngineerManager作为Employee的子类。每个子类包含代表其所代表子类的唯一属性的列。每个表还必须包含主键列(或列)以及对父表的外键引用:

class Engineer(Employee):
    __tablename__ = "engineer"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    engineer_name: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

在上面的示例中,每个映射在其映射器参数中指定了Mapper.polymorphic_identity参数。此值填充了由基本映射器上建立的Mapper.polymorphic_on参数指定的列。Mapper.polymorphic_identity参数应该对整个层次结构中的每个映射类是唯一的,并且每个映射类只应有一个“标识”;如上所述,不支持一些子类引入第二个标识的“级联”标识。

ORM 使用Mapper.polymorphic_identity设置的值来确定加载行时行属于哪个类。在上面的示例中,每个代表Employee的行在其type列中将有值'employee';同样,每个Engineer将获得值'engineer',每个Manager将获得值'manager'。无论继承映射使用不同的联接表作为子类(如联合表继承)还是所有一个表作为单表继承,这个值都应该被持久化并在查询时对 ORM 可用。Mapper.polymorphic_identity参数也适用于具体表继承,但实际上并没有被持久化;有关详细信息,请参阅后面的具体表继承部分。

在多态设置中,最常见的是外键约束建立在与主键本身相同的列或列上,但这并非必需;一个与主键不同的列也可以通过外键指向父类。从基本表到子类构建 JOIN 的方式也是可以直接自定义的,但这很少是必要的。

完成联合继承映射后,针对Employee的查询将返回EmployeeEngineerManager对象的组合。新保存的EngineerManagerEmployee对象将自动填充employee.type列,此时正确的“鉴别器”值为"engineer""manager""employee"

具有联合继承关系

与联合表继承完全支持关系。涉及联合继承类的关系应该针对与外键约束对应的层次结构中的类;在下面的示例中,由于employee表有一个指向company表的外键约束,关系被建立在CompanyEmployee之间:

from __future__ import annotations

from sqlalchemy.orm import relationship

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee): ...

class Engineer(Employee): ...

如果外键约束在对应于子类的表上,则关系应该指向该子类。在下面的示例中,从managercompany有一个外键约束,因此建立了ManagerCompany类之间的关系:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee): ...

在上面,Manager类将具有Manager.company属性;Company将具有Company.managers属性,总是针对employeemanager表一起加载的连接进行加载。

加载连接继承映射

请参阅编写用于继承映射的 SELECT 语句部分,了解继承加载技术的背景,包括在映射器配置时间和查询时间配置要查询的表。

具有连接继承的关系

与连接表继承完全支持关系。涉及连接继承类的关系应该指向与外键约束对应的层次结构中的类;在下面的示例中,由于employee表有一个指向company表的外键约束,因此在CompanyEmployee之间建立了关系:

from __future__ import annotations

from sqlalchemy.orm import relationship

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee): ...

class Engineer(Employee): ...

如果外键约束在对应于子类的表上,则关系应该指向该子类。在下面的示例中,从managercompany有一个外键约束,因此建立了ManagerCompany类之间的关系:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee): ...

在上面,Manager类将具有Manager.company属性;Company将具有Company.managers属性,总是针对employeemanager表一起加载的连接进行加载。

加载连接继承映射

请参阅编写用于继承映射的 SELECT 语句部分,了解继承加载技术的背景,包括在映射器配置时间和查询时间配置要查询的表。

单表继承

单表继承将所有子类的所有属性表示为单个表中的内容。具有特定类别属性的特定子类将在表中的列中保留它们,如果行引用不同类型的对象,则列中将为空。

在层次结构中查询特定子类将呈现为针对基表的 SELECT,其中将包括一个 WHERE 子句,该子句将限制行为具有鉴别器列或表达式中存在的特定值或值。

单表继承相对于连接表继承具有简单性的优势;查询效率更高,因为只需要涉及一个表来加载每个表示类的对象。

单表继承配置看起来很像连接表继承,只是基类指定了__tablename__。基表还需要一个鉴别器列,以便类之间可以区分开来。

即使子类共享所有属性的基本表,当使用 Declarative 时,仍然可以在子类上指定 mapped_column 对象,指示该列仅映射到该子类;mapped_column 将应用于相同的基本 Table 对象:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Manager(Employee):
    manager_data: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

注意到派生类 Manager 和 Engineer 的映射器省略了 __tablename__,表明它们没有自己的映射表。此外,还包括一个带有 nullable=Truemapped_column() 指令;由于为这些类声明的 Python 类型不包括 Optional[],该列通常会被映射为 NOT NULL,这对于只期望为对应于特定子类的那些行填充的列来说是不合适的。

使用 use_existing_column 解决列冲突

注意在前一节中,manager_nameengineer_info 列被“上移”应用到 Employee.__table__,因为它们在没有自己表的子类上声明。当两个子类想要指定相同列时,就会出现一个棘手的情况,如下所示:

from datetime import datetime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

上面,在 EngineerManager 上声明的 start_date 列将导致错误:

sqlalchemy.exc.ArgumentError: Column 'start_date' on class Manager conflicts
with existing column 'employee.start_date'.  If using Declarative,
consider using the use_existing_column parameter of mapped_column() to
resolve conflicts.

上述情况对 Declarative 映射系统提出了一个模棱两可的问题,可以通过在 mapped_column() 上使用 mapped_column.use_existing_column 参数来解决,该参数指示 mapped_column() 查找并使用已经映射的继承超类上的列,如果已经存在,否则映射一个新列:

from sqlalchemy import DateTime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

在上面的例子中,当Manager被映射时,start_date列已经存在于Employee类上,已经由Engineer映射提供。mapped_column.use_existing_column参数指示mapped_column()应该首先在Employee的映射Table上查找请求的Column,如果存在,则保持该现有映射。如果不存在,mapped_column()将正常映射该列,将其添加为Employee超类引用的Table中的列之一。

2.0.0b4 版本中新增:- 添加了mapped_column.use_existing_column,提供了一种符合 2.0 版本的方式来有条件地映射继承子类上的列。之前的方法结合了declared_attr和对父类.__table__的查找仍然有效,但缺乏PEP 484类型支持。

类似的概念可以与混合类一起使用(参见使用混合类组合映射层次结构)来定义一系列特定的列和/或其他可重用混合类中的映射属性:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": type,
        "polymorphic_identity": "employee",
    }

class HasStartDate:
    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Engineer(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

class Manager(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

与单表继承的关系

与单表继承完全支持关系。配置方式与连接继承相同;外键属性应该在与关系的“外键”一侧相同的类上:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_data: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

此外,与连接继承的情况类似,我们可以创建涉及特定子类的关系。在查询时,SELECT 语句将包含一个 WHERE 子句,将类选择限制为该子类或子类:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_name: Mapped[str] = mapped_column(nullable=True)

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

在上面的例子中,Manager类将具有Manager.company属性;Company将具有Company.managers属性,始终针对具有额外 WHERE 子句的employee加载,该子句将行限制为type = 'manager'的行。

使用polymorphic_abstract构建更深层次的层次结构

2.0 版本中新增。

在构建任何继承层次结构时,一个映射类可以设置Mapper.polymorphic_abstract参数为True,这表示该类应该被正常映射,但不希望直接实例化,并且不包含Mapper.polymorphic_identity。然后可以声明这个映射类的子类,这些子类可以包含Mapper.polymorphic_identity,因此可以被正常使用。这允许一系列子类被一个公共基类引用,该基类在层次结构中被认为是“抽象的”,无论是在查询中还是在relationship()声明中。这种用法与在 Declarative 中使用 abstract 属性的用法不同,后者将目标类完全未映射,因此不能单独作为映射类使用。Mapper.polymorphic_abstract可以应用于层次结构中的任何类或类,包括同时在多个级别上应用。

举例来说,假设ManagerPrincipal都被分类到一个超类Executive下,而EngineerSysadmin被分类到一个超类Technologist下。ExecutiveTechnologist都不会被实例化,因此没有Mapper.polymorphic_identity。这些类可以通过Mapper.polymorphic_abstract进行配置如下:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Executive(Employee):
  """An executive of the company"""

    executive_background: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Technologist(Employee):
  """An employee who works with technology"""

    competencies: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Manager(Executive):
  """a manager"""

    __mapper_args__ = {"polymorphic_identity": "manager"}

class Principal(Executive):
  """a principal of the company"""

    __mapper_args__ = {"polymorphic_identity": "principal"}

class Engineer(Technologist):
  """an engineer"""

    __mapper_args__ = {"polymorphic_identity": "engineer"}

class SysAdmin(Technologist):
  """a systems administrator"""

    __mapper_args__ = {"polymorphic_identity": "sysadmin"}

在上面的例子中,新的类TechnologistExecutive都是普通的映射类,并且指示要添加到超类中的新列executive_backgroundcompetencies。然而,它们都缺少Mapper.polymorphic_identity的设置;这是因为不希望直接实例化TechnologistExecutive;我们总是会有ManagerPrincipalEngineerSysAdmin中的一个。但是我们可以查询PrincipalTechnologist角色,并且让它们成为relationship()的目标。下面的示例演示了针对Technologist对象的 SELECT 语句:

session.scalars(select(Technologist)).all()
SELECT  employee.id,  employee.name,  employee.type,  employee.competencies
FROM  employee
WHERE  employee.type  IN  (?,  ?)
[...]  ('engineer',  'sysadmin') 

TechnologistExecutive抽象映射类也可以成为relationship()映射的目标,就像任何其他映射类一样。我们可以扩展上面的例子,包括Company,有单独的集合Company.technologistsCompany.principals

class Company(Base):
    __tablename__ = "company"
    id = Column(Integer, primary_key=True)

    executives: Mapped[List[Executive]] = relationship()
    technologists: Mapped[List[Technologist]] = relationship()

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)

    # foreign key to "company.id" is added
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))

    # rest of mapping is the same
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
    }

# Executive, Technologist, Manager, Principal, Engineer, SysAdmin
# classes from previous example would follow here unchanged

使用上述映射,我们可以分别跨Company.technologistsCompany.executives使用连接和关系加载技术:

session.scalars(
    select(Company)
    .join(Company.technologists)
    .where(Technologist.competency.ilike("%java%"))
    .options(selectinload(Company.executives))
).all()
SELECT  company.id
FROM  company  JOIN  employee  ON  company.id  =  employee.company_id  AND  employee.type  IN  (?,  ?)
WHERE  lower(employee.competencies)  LIKE  lower(?)
[...]  ('engineer',  'sysadmin',  '%java%')

SELECT  employee.company_id  AS  employee_company_id,  employee.id  AS  employee_id,
employee.name  AS  employee_name,  employee.type  AS  employee_type,
employee.executive_background  AS  employee_executive_background
FROM  employee
WHERE  employee.company_id  IN  (?)  AND  employee.type  IN  (?,  ?)
[...]  (1,  'manager',  'principal') 

另请参阅

abstract - 声明性参数,允许声明性类在层次结构中完全取消映射,同时仍然从映射的超类扩展。

加载单一继承映射

单表继承的加载技术与联接表继承的加载技术基本相同,并且在这两种映射类型之间提供了高度的抽象,使得很容易在它们之间进行切换,以及在单个层次结构中混合使用它们(只需从要单继承的子类中省略 __tablename__)。请参阅 编写继承映射的 SELECT 语句 和 单一继承映射的 SELECT 语句 部分,了解有关继承加载技术的文档,包括在映射器配置时间和查询时间配置要查询的类。

使用 use_existing_column 解决列冲突

在上一节中注意到,manager_nameengineer_info列被“上移”,应用于Employee.__table__,因为它们在没有自己的表的子类上声明。当两个子类想要指定相同列时会出现棘手的情况,如下所示:

from datetime import datetime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }
    start_date: Mapped[datetime] = mapped_column(nullable=True)

在上述代码中,同时在EngineerManager上声明的start_date列将导致错误:

sqlalchemy.exc.ArgumentError: Column 'start_date' on class Manager conflicts
with existing column 'employee.start_date'.  If using Declarative,
consider using the use_existing_column parameter of mapped_column() to
resolve conflicts.

上述情景对声明性映射系统存在一种模糊性,可以通过在mapped_column()上使用mapped_column.use_existing_column参数来解决,该参数指示mapped_column()查找继承的超类,并使用已经存在的列,如果已经存在,则映射新列:

from sqlalchemy import DateTime

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Manager(Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

在上文中,当 Manager 被映射时,start_date 列已经存在于 Employee 类中,已经由之前的 Engineer 映射提供。mapped_column.use_existing_column 参数指示给 mapped_column(),它应该首先查找映射到 Employee 的映射 Table 上的请求的 Column,如果存在,则保持该现有映射。如果不存在,mapped_column() 将正常映射列,将其添加为 Employee 超类引用的 Table 中的列之一。

新版本 2.0.0b4 中新增:- 添加了 mapped_column.use_existing_column,它提供了一个与 2.0 兼容的方法,以条件地映射继承子类上的列。先前的方法结合了 declared_attr 与对父类 .__table__ 的查找,仍然有效,但缺少了 PEP 484 类型支持。

一个类似的概念可以与 mixin 类一起使用(参见 Composing Mapped Hierarchies with Mixins)来定义来自可重用 mixin 类的特定系列列和/或其他映射属性:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": type,
        "polymorphic_identity": "employee",
    }

class HasStartDate:
    start_date: Mapped[datetime] = mapped_column(
        nullable=True, use_existing_column=True
    )

class Engineer(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

class Manager(HasStartDate, Employee):
    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

单表继承的关系

关系在单表继承中得到充分支持。配置方式与联接继承的方式相同;外键属性应位于与关系的“外部”一侧相同的类上:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    employees: Mapped[List[Employee]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_data: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

类似于联接继承的情况,我们也可以创建涉及特定子类的关系。当查询时,SELECT 语句将包含一个 WHERE 子句,将类的选择限制为该子类或子类:

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Manager(Employee):
    manager_name: Mapped[str] = mapped_column(nullable=True)

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    engineer_info: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

上文中,Manager 类将具有一个 Manager.company 属性;Company 将具有一个 Company.managers 属性,该属性始终加载针对具有附加 WHERE 子句的 employee,限制行为具有 type = 'manager' 的行。

使用 polymorphic_abstract 构建更深层次的层次结构

新版本 2.0 中新增。

在构建任何类型的继承层次结构时,映射类可以包含设置为TrueMapper.polymorphic_abstract参数,表示该类应该正常映射,但不期望直接实例化,并且不包括Mapper.polymorphic_identity。然后可以声明这个映射类的子类,这些子类本身可以包含Mapper.polymorphic_identity,因此可以正常使用。这允许一系列子类被一个被认为是层次结构内“抽象”的公共基类引用,无论是在查询中还是在relationship()声明中。这种用法与在 Declarative 中使用 abstract 属性的用法不同,后者使目标类完全未映射,因此不能作为一个映射类单独使用。Mapper.polymorphic_abstract可以应用于层次结构中的任何类或类,包括一次在多个级别上。

举个例子,假设要将ManagerPrincipal都归类到一个超类Executive下,而EngineerSysadmin则归类到一个超类Technologist下。ExecutiveTechnologist都不会被实例化,因此没有Mapper.polymorphic_identity。可以使用Mapper.polymorphic_abstract来配置这些类,如下所示:

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

class Executive(Employee):
  """An executive of the company"""

    executive_background: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Technologist(Employee):
  """An employee who works with technology"""

    competencies: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {"polymorphic_abstract": True}

class Manager(Executive):
  """a manager"""

    __mapper_args__ = {"polymorphic_identity": "manager"}

class Principal(Executive):
  """a principal of the company"""

    __mapper_args__ = {"polymorphic_identity": "principal"}

class Engineer(Technologist):
  """an engineer"""

    __mapper_args__ = {"polymorphic_identity": "engineer"}

class SysAdmin(Technologist):
  """a systems administrator"""

    __mapper_args__ = {"polymorphic_identity": "sysadmin"}

在上面的示例中,新类TechnologistExecutive都是普通的映射类,并指示要添加到超类中的新列executive_backgroundcompetencies。然而,它们都缺少Mapper.polymorphic_identity的设置;这是因为不期望直接实例化TechnologistExecutive;我们总是会有ManagerPrincipalEngineerSysAdmin中的一个。然而,我们可以查询PrincipalTechnologist角色,并使它们成为relationship()的目标。下面的示例演示了用于Technologist对象的 SELECT 语句:

session.scalars(select(Technologist)).all()
SELECT  employee.id,  employee.name,  employee.type,  employee.competencies
FROM  employee
WHERE  employee.type  IN  (?,  ?)
[...]  ('engineer',  'sysadmin') 

抽象映射的TechnologistExecutive抽象映射类也可以成为relationship()映射的目标,就像任何其他映射类一样。我们可以扩展上述示例以包括Company,并分别添加Company.technologistsCompany.principals两个集合:

class Company(Base):
    __tablename__ = "company"
    id = Column(Integer, primary_key=True)

    executives: Mapped[List[Executive]] = relationship()
    technologists: Mapped[List[Technologist]] = relationship()

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)

    # foreign key to "company.id" is added
    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))

    # rest of mapping is the same
    name: Mapped[str]
    type: Mapped[str]

    __mapper_args__ = {
        "polymorphic_on": "type",
    }

# Executive, Technologist, Manager, Principal, Engineer, SysAdmin
# classes from previous example would follow here unchanged

使用上述映射,我们可以分别在Company.technologistsCompany.executives之间使用连接和关系加载技术:

session.scalars(
    select(Company)
    .join(Company.technologists)
    .where(Technologist.competency.ilike("%java%"))
    .options(selectinload(Company.executives))
).all()
SELECT  company.id
FROM  company  JOIN  employee  ON  company.id  =  employee.company_id  AND  employee.type  IN  (?,  ?)
WHERE  lower(employee.competencies)  LIKE  lower(?)
[...]  ('engineer',  'sysadmin',  '%java%')

SELECT  employee.company_id  AS  employee_company_id,  employee.id  AS  employee_id,
employee.name  AS  employee_name,  employee.type  AS  employee_type,
employee.executive_background  AS  employee_executive_background
FROM  employee
WHERE  employee.company_id  IN  (?)  AND  employee.type  IN  (?,  ?)
[...]  (1,  'manager',  'principal') 

另请参见

abstract - 声明性参数,允许在层次结构中完全取消映射 Declarative 类,同时仍从映射的超类扩展。

加载单继承映射

单表继承的加载技术大部分与用于连接表继承的技术相同,并且在这两种映射类型之间提供了很高程度的抽象,因此很容易在它们之间进行切换以及在单个层次结构中混合使用它们(只需从要单继承的子类中省略__tablename__)。请参阅编写继承映射的 SELECT 语句和单继承映射的 SELECT 语句章节,了解有关继承加载技术的文档,包括在映射器配置时间和查询时间配置要查询的类。

具体表继承

具体表继承将每个子类映射到其自己的独立表格,每个表格包含产生该类实例所需的所有列。具体继承配置默认情况下进行非多态查询;对于特定类的查询只会查询该类的表格,并且只返回该类的实例。具体类的多态加载通过在映射器内配置一个特殊的 SELECT 来启用,该 SELECT 通常被生成为所有表的 UNION。

警告

具体表继承比连接或单表继承更加复杂,在功能上更加受限,特别是在使用关系、急加载和多态加载方面。当以多态方式使用时,会产生非常庞大的查询,其中包含的 UNION 操作不会像简单的连接那样执行良好。强烈建议如果需要灵活性的关系加载和多态加载,尽可能使用连接或单表继承。如果不需要多态加载,则可以使用普通的非继承映射,如果每个类都完全引用其自己的表格。

虽然联接和单表继承在“多态”加载方面很流畅,但在具体继承中却是一种更笨拙的事情。因此,当不需要多态加载时,具体继承更为适用。建立涉及具体继承类的关系也更加麻烦。

要将类建立为使用具体继承,请在__mapper_args__中添加Mapper.concrete参数。这既表示对声明式以及映射,超类表不应被视为映射的一部分:

class Employee(Base):
    __tablename__ = "employee"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))

class Manager(Employee):
    __tablename__ = "manager"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(50))

    __mapper_args__ = {
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(50))

    __mapper_args__ = {
        "concrete": True,
    }

有两个关键点需要注意:

  • 我们必须在每个子类上显式定义所有列,甚至是同名的列。例如,此处的Employee.name不会被复制到由我们映射的ManagerEngineer表中。

  • 虽然EngineerManager类在与Employee的继承关系中被映射,但它们仍然不包括多态加载。也就是说,如果我们查询Employee对象,managerengineer表根本不会被查询。

具体多态加载配置

具有具体继承的多态加载要求针对应该具有多态加载的每个基类配置专门的 SELECT。此 SELECT 需要能够单独访问所有映射的表,并且通常是使用 SQLAlchemy 辅助程序polymorphic_union()构造的 UNION 语句。

如编写继承映射的 SELECT 语句所述,任何类型的映射器继承配置都可以使用Mapper.with_polymorphic参数默认配置从特殊的可选项中加载。当前公共 API 要求在首次构造Mapper时设置此参数。

但是,在声明式的情况下,映射器和被映射的Table同时创建,即在定义映射类的那一刻。这意味着暂时无法提供Mapper.with_polymorphic参数,因为子类对应的Table对象尚未定义。

有一些可用的策略来解决这个循环,然而声明性提供了帮助类ConcreteBaseAbstractConcreteBase,它们在幕后处理这个问题。

使用ConcreteBase,我们几乎可以以与其他形式的继承映射相同的方式设置我们的具体映射:

from sqlalchemy.ext.declarative import ConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

在上述示例中,声明性在映射器“初始化”时为Employee类设置了多态可选择项;这是解析其他依赖映射器的映射器的后配置步骤。ConcreteBase 帮助程序使用polymorphic_union()函数在设置了其他所有类之后创建所有具体映射表的 UNION,然后使用已经存在的基类映射器配置此语句。

在选择时,多态联合产生这样的查询:

session.scalars(select(Employee)).all()
SELECT
  pjoin.id,
  pjoin.name,
  pjoin.type,
  pjoin.manager_data,
  pjoin.engineer_info
FROM  (
  SELECT
  employee.id  AS  id,
  employee.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'employee'  AS  type
  FROM  employee
  UNION  ALL
  SELECT
  manager.id  AS  id,
  manager.name  AS  name,
  manager.manager_data  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'manager'  AS  type
  FROM  manager
  UNION  ALL
  SELECT
  engineer.id  AS  id,
  engineer.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  engineer.engineer_info  AS  engineer_info,
  'engineer'  AS  type
  FROM  engineer
)  AS  pjoin 

上述 UNION 查询需要为每个子表制造“NULL”列,以容纳那些不属于特定子类的列。

另请参阅

ConcreteBase ### 抽象具体类

到目前为止,所示的具体映射同时显示了子类和基类映射到各自的表中。在具体继承用例中,常见的是基类在数据库中没有表示,只有子类。换句话说,基类是“抽象的”。

通常,当一个人想要将两个不同的子类映射到各自的表中,并且保留基类未映射时,这可以非常容易地实现。在使用声明性时,只需使用__abstract__指示器声明基类:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(Base):
    __abstract__ = True

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

在上述示例中,我们实际上没有利用 SQLAlchemy 的继承映射功能;我们可以正常加载和持久化ManagerEngineer的实例。然而,当我们需要多态地查询时,也就是说,我们想要发出select(Employee)并返回ManagerEngineer实例的集合时,情况就会发生变化。这让我们重新进入具体继承的领域,我们必须针对Employee构建一个特殊的映射器才能实现这一点。

要修改我们的具体继承示例以说明一个能够进行多态加载的“抽象”基类,我们将只有一个 engineer 和一个 manager 表,没有 employee 表,但是 Employee 映射器将直接映射到“多态联合”,而不是在 Mapper.with_polymorphic 参数中本地指定它。

为了帮助实现这一点,声明性提供了一个名为 AbstractConcreteBaseConcreteBase 类的变体,它可以自动实现这一点:

from sqlalchemy.ext.declarative import AbstractConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    name = mapped_column(String(50))

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

Base.registry.configure()

在上面的代码中,调用了 registry.configure() 方法,这将触发 Employee 类实际上被映射;在配置步骤之前,由于尚未定义将从中查询的子表,因此该类没有映射。这个过程比 ConcreteBase 更复杂,因为整个基类的映射必须延迟到所有子类都声明完毕。有了上述的映射,只有 ManagerEngineer 的实例才能被持久化;对 Employee 类进行查询将始终产生 ManagerEngineer 对象。

使用上述的映射,可以根据 Employee 类和在其上局部声明的任何属性生成查询,例如 Employee.name

>>> stmt = select(Employee).where(Employee.name == "n1")
>>> print(stmt)
SELECT  pjoin.id,  pjoin.name,  pjoin.type,  pjoin.manager_data,  pjoin.engineer_info
FROM  (
  SELECT  engineer.id  AS  id,  engineer.name  AS  name,  engineer.engineer_info  AS  engineer_info,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,  'engineer'  AS  type
  FROM  engineer
  UNION  ALL
  SELECT  manager.id  AS  id,  manager.name  AS  name,  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  manager.manager_data  AS  manager_data,  'manager'  AS  type
  FROM  manager
)  AS  pjoin
WHERE  pjoin.name  =  :name_1 

AbstractConcreteBase.strict_attrs 参数表示 Employee 类应直接映射仅属于 Employee 类本身的属性,例如 Employee.name 属性。其他属性如 Manager.manager_dataEngineer.engineer_info 仅存在于它们各自的子类中。当未设置 AbstractConcreteBase.strict_attrs 时,所有子类的属性如 Manager.manager_dataEngineer.engineer_info 都会映射到基类 Employee 中。这是一种遗留模式,对于查询可能更方便,但会导致所有子类共享整个层次结构的完整属性集;在上述示例中,如果不使用 AbstractConcreteBase.strict_attrs,将会生成无用的 Engineer.manager_nameManager.engineer_info 属性。

新版本 2.0 中:增加了 AbstractConcreteBase.strict_attrs 参数到 AbstractConcreteBase,以生成更清晰的映射;默认值为 False,以允许遗留映射在 1.x 版本中继续正常工作。

另请参阅

AbstractConcreteBase

经典和半经典具体多态配置

通过ConcreteBaseAbstractConcreteBase说明的声明性配置等同于另外两种明确使用polymorphic_union()的配置形式。这些配置形式明确使用Table对象,以便首先创建“多态联合”,然后应用到映射中。这些例子旨在澄清polymorphic_union()函数在映射中的作用。

例如,半经典映射利用了声明性,但是单独建立了Table对象:

metadata_obj = Base.metadata

employees_table = Table(
    "employee",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
)

managers_table = Table(
    "manager",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("manager_data", String(50)),
)

engineers_table = Table(
    "engineer",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("engineer_info", String(50)),
)

接下来,使用polymorphic_union()生成 UNION:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "employee": employees_table,
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

使用上述Table对象,可以使用“半经典”风格生成映射,在这种风格中,我们将声明性与__table__参数结合使用;我们上面的多态联合通过__mapper_args__传递给Mapper.with_polymorphic参数:

class Employee(Base):
    __table__ = employee_table
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": ("*", pjoin),
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

或者,可以完全以“经典”风格使用相同的Table对象,而不使用声明性。与声明性提供的类似的构造函数如下所示:

class Employee:
    def __init__(self, **kw):
        for k in kw:
            setattr(self, k, kw[k])

class Manager(Employee):
    pass

class Engineer(Employee):
    pass

employee_mapper = mapper_registry.map_imperatively(
    Employee,
    pjoin,
    with_polymorphic=("*", pjoin),
    polymorphic_on=pjoin.c.type,
)
manager_mapper = mapper_registry.map_imperatively(
    Manager,
    managers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="manager",
)
engineer_mapper = mapper_registry.map_imperatively(
    Engineer,
    engineers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="engineer",
)

“抽象”示例也可以使用“半经典”或“经典”风格进行映射。不同之处在于,我们将“多态联合”应用于Mapper.with_polymorphic参数的方式,而是将其直接应用于我们基本映射器上的映射可选项。半经典映射如下所示:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

class Employee(Base):
    __table__ = pjoin
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": "*",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

在上面的例子中,我们像以前一样使用polymorphic_union(),只是省略了employee表。

另请参阅

命令式映射 - 命令式或“经典”映射的背景信息

具体继承的关系

在具体继承的情况下,映射关系是具有挑战性的,因为不同的类不共享表格。如果关系仅涉及特定类,例如在我们先前的示例中CompanyManager之间的关系,那么不需要特殊步骤,因为这只是两个相关的表。

但是,如果Company要对Employee有一对多的关系,表明集合可能包含EngineerManager对象,那么这意味着Employee必须具有多态加载功能,并且要与company表关联的每个表都必须有一个外键。这种配置的示例如下:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

具体继承和关系的下一个复杂性涉及当我们希望EmployeeManagerEngineer中的一个或全部自己参考Company时。对于这种情况,SQLAlchemy 具有特殊行为,即在Employee上放置到Companyrelationship()在实例级别时不适用ManagerEngineer类。相反,必须对每个类应用不同的relationship()。为了在三个单独的关系中实现与Company.employees相反的双向行为,使用了relationship.back_populates参数:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee", back_populates="company")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

上述限制与当前的实现相关,其中具体继承类不共享超类的任何属性,因此需要设置不同的关系。

加载具体继承映射

具体继承的加载选项有限;一般来说,如果使用声明性具体混合类型之一在映射器上配置多态加载,那么在当前 SQLAlchemy 版本中无法在查询时修改它。通常,with_polymorphic()函数应该能够覆盖具体使用的加载方式,但由于当前的限制,这尚不受支持。

具体多态加载配置

具有具体继承的多态加载要求针对应该具有多态加载的每个基类配置一个专用的 SELECT。这个 SELECT 需要能够单独访问所有映射的表,并且通常是使用 SQLAlchemy 助手polymorphic_union()构造的 UNION 语句。

如为继承映射编写 SELECT 语句所讨论的,任何类型的映射器继承配置都可以使用Mapper.with_polymorphic参数默认配置从特殊的可选项加载。当前的公共 API 要求在首次构造Mapper时设置此参数。

但是,在声明式编程中,映射器和被映射的Table同时创建,即在定义映射类的时候。这意味着Mapper.with_polymorphic参数还不能提供,因为对应于子类的Table对象尚未定义。

有几种策略可用于解决这种循环,然而,声明式提供了处理此问题的助手类ConcreteBaseAbstractConcreteBase

使用ConcreteBase,我们可以几乎与其他形式的继承映射方式相同地设置我们的具体映射:

from sqlalchemy.ext.declarative import ConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

在上述情况下,声明式在映射器“初始化”时为Employee类设置多态可选项;这是为解析其他依赖映射器而进行的映射器的后期配置步骤。ConcreteBase助手使用polymorphic_union()函数在设置完所有其他类之后创建所有具体映射表的联合,然后使用已经存在的基类映射器配置此语句。

在选择时,多态联合产生类似这样的查询:

session.scalars(select(Employee)).all()
SELECT
  pjoin.id,
  pjoin.name,
  pjoin.type,
  pjoin.manager_data,
  pjoin.engineer_info
FROM  (
  SELECT
  employee.id  AS  id,
  employee.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'employee'  AS  type
  FROM  employee
  UNION  ALL
  SELECT
  manager.id  AS  id,
  manager.name  AS  name,
  manager.manager_data  AS  manager_data,
  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  'manager'  AS  type
  FROM  manager
  UNION  ALL
  SELECT
  engineer.id  AS  id,
  engineer.name  AS  name,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,
  engineer.engineer_info  AS  engineer_info,
  'engineer'  AS  type
  FROM  engineer
)  AS  pjoin 

上述的 UNION 查询需要为每个子表制造“NULL”列,以适应那些不是特定子类成员的列。

另请参见

ConcreteBase

抽象具体类

到目前为止,所示的具体映射显示了子类以及基类映射到单独的表中。在具体继承用例中,常见的情况是基类在数据库中不表示,只有子类。换句话说,基类是“抽象的”。

通常,当一个人想要将两个不同的子类映射到单独的表中,并且保留基类未映射时,这可以非常容易地实现。当使用声明式时,只需使用__abstract__指示符声明基类:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(Base):
    __abstract__ = True

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

在上述代码中,我们实际上没有使用 SQLAlchemy 的继承映射功能;我们可以正常加载和持久化ManagerEngineer的实例。然而,当我们需要进行多态查询时,情况就会发生变化,也就是说,我们希望发出select(Employee)并返回一组ManagerEngineer实例。这将我们带回到具体继承的领域,我们必须构建一个针对Employee的特殊映射器才能实现这一点。

要修改我们的具体继承示例,以说明能够进行多态加载的“抽象”基类,我们将只有一个engineer和一个manager表,而没有employee表,但Employee映射器将直接映射到“多态联合”,而不是将其局部指定给Mapper.with_polymorphic参数。

为了帮助解决这个问题,Declarative 提供了一种名为AbstractConcreteBaseConcreteBase类的变体,可以自动实现这一点:

from sqlalchemy.ext.declarative import AbstractConcreteBase
from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    name = mapped_column(String(50))

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

Base.registry.configure()

在上面的代码中,调用了registry.configure()方法,这将触发Employee类实际映射;在配置步骤之前,该类没有映射,因为它将从中查询的子表尚未定义。这个过程比ConcreteBase的过程更复杂,因为必须延迟基类的整个映射,直到所有的子类都已声明。通过像上面这样的映射,只能持久化ManagerEngineer的实例;对Employee类进行查询将始终生成ManagerEngineer对象。

使用上述映射,可以按照Employee类和在其上本地声明的任何属性生成查询,例如Employee.name

>>> stmt = select(Employee).where(Employee.name == "n1")
>>> print(stmt)
SELECT  pjoin.id,  pjoin.name,  pjoin.type,  pjoin.manager_data,  pjoin.engineer_info
FROM  (
  SELECT  engineer.id  AS  id,  engineer.name  AS  name,  engineer.engineer_info  AS  engineer_info,
  CAST(NULL  AS  VARCHAR(40))  AS  manager_data,  'engineer'  AS  type
  FROM  engineer
  UNION  ALL
  SELECT  manager.id  AS  id,  manager.name  AS  name,  CAST(NULL  AS  VARCHAR(40))  AS  engineer_info,
  manager.manager_data  AS  manager_data,  'manager'  AS  type
  FROM  manager
)  AS  pjoin
WHERE  pjoin.name  =  :name_1 

AbstractConcreteBase.strict_attrs 参数指示 Employee 类应直接映射仅属于 Employee 类的本地属性,即 Employee.name 属性。其他属性,如 Manager.manager_dataEngineer.engineer_info,仅存在于其对应的子类中。当未设置 AbstractConcreteBase.strict_attrs 时,所有子类属性,如 Manager.manager_dataEngineer.engineer_info,都映射到基类 Employee。这是一种传统的使用模式,可能更方便查询,但其效果是所有子类共享整个层次结构的完整属性集;在上述示例中,不使用 AbstractConcreteBase.strict_attrs 将导致生成不必要的 Engineer.manager_nameManager.engineer_info 属性。

2.0 版本新增:增加了 AbstractConcreteBase.strict_attrs 参数到 AbstractConcreteBase 中,以产生更清晰的映射;默认值为 False,以允许传统映射继续像 1.x 版本中那样工作。

另请参阅

AbstractConcreteBase

经典和半经典具有多态性的具体配置

ConcreteBaseAbstractConcreteBase说明的声明性配置等同于另外两种明确使用polymorphic_union()的配置形式。这些配置形式明确使用Table对象,以便首先创建“多态联合”,然后应用于映射。这里举例说明了这些配置形式,以阐明polymorphic_union()函数在映射方面的作用。

一个半经典映射的例子利用了声明性,但是单独建立了Table对象:

metadata_obj = Base.metadata

employees_table = Table(
    "employee",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
)

managers_table = Table(
    "manager",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("manager_data", String(50)),
)

engineers_table = Table(
    "engineer",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("engineer_info", String(50)),
)

接下来,使用polymorphic_union()生成 UNION:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "employee": employees_table,
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

使用上述Table对象,可以使用“半经典”风格生成映射,其中我们与__table__参数一起使用声明性;我们上面的多态联合通过__mapper_args__传递给Mapper.with_polymorphic参数:

class Employee(Base):
    __table__ = employee_table
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": ("*", pjoin),
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

或者,完全可以使用完全“经典”风格,而根本不使用声明性,使用与声明性提供的类似的构造函数:

class Employee:
    def __init__(self, **kw):
        for k in kw:
            setattr(self, k, kw[k])

class Manager(Employee):
    pass

class Engineer(Employee):
    pass

employee_mapper = mapper_registry.map_imperatively(
    Employee,
    pjoin,
    with_polymorphic=("*", pjoin),
    polymorphic_on=pjoin.c.type,
)
manager_mapper = mapper_registry.map_imperatively(
    Manager,
    managers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="manager",
)
engineer_mapper = mapper_registry.map_imperatively(
    Engineer,
    engineers_table,
    inherits=employee_mapper,
    concrete=True,
    polymorphic_identity="engineer",
)

“抽象”示例也可以使用“半经典”或“经典”风格进行映射。不同之处在于,我们不是将“多态联合”应用于Mapper.with_polymorphic参数,而是直接将其应用为我们最基本的映射器上的映射选择器。下面是半经典映射的示例:

from sqlalchemy.orm import polymorphic_union

pjoin = polymorphic_union(
    {
        "manager": managers_table,
        "engineer": engineers_table,
    },
    "type",
    "pjoin",
)

class Employee(Base):
    __table__ = pjoin
    __mapper_args__ = {
        "polymorphic_on": pjoin.c.type,
        "with_polymorphic": "*",
        "polymorphic_identity": "employee",
    }

class Engineer(Employee):
    __table__ = engineer_table
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

class Manager(Employee):
    __table__ = manager_table
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

在上面的示例中,我们像以前一样使用polymorphic_union(),只是省略了employee表。

另请参见

命令式映射 - 关于命令式或“经典”映射的背景信息

具体继承关系

在具体继承的情况下,映射关系是具有挑战性的,因为不同的类不共享一个表。如果关系只涉及特定的类,比如我们之前示例中的 CompanyManager 之间的关系,那么不需要特殊步骤,因为这只是两个相关的表。

然而,如果 Company 要与 Employee 有一对多的关系,表明集合可能包括 EngineerManager 对象,这意味着 Employee 必须具有多态加载能力,并且每个相关的表都必须有一个外键返回到 company 表。这样的配置示例如下:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

具体继承和关系的下一个复杂性涉及当我们希望 EmployeeManagerEngineer 中的一个或全部自身参考 Company 时。对于这种情况,SQLAlchemy 在 Employee 上有特殊的行为,即一个链接到 Companyrelationship() 放置在 Employee 上,当在实例级别执行时,不适用ManagerEngineer 类。相反,必须对每个类应用不同的 relationship()。为了实现作为 Company.employees 的相反的三个单独关系的双向行为,使用了 relationship.back_populates 参数:

from sqlalchemy.ext.declarative import ConcreteBase

class Company(Base):
    __tablename__ = "company"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    employees = relationship("Employee", back_populates="company")

class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }

class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    manager_data = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    engineer_info = mapped_column(String(40))
    company_id = mapped_column(ForeignKey("company.id"))
    company = relationship("Company", back_populates="employees")

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "concrete": True,
    }

上述限制与当前实现相关,包括具体继承类不共享超类的任何属性,因此需要设置不同的关系。

加载具体继承映射

具体继承加载选项有限;一般来说,如果在映射器上配置了多态加载,使用其中一个声明性的具体混合类,那么在当前的 SQLAlchemy 版本中它就不能在查询时进行修改。通常,with_polymorphic() 函数应该能够覆盖具体加载使用的样式,然而由于当前的限制,这还不被支持。

非传统映射

原文:docs.sqlalchemy.org/en/20/orm/nonstandard_mappings.html

将类映射到多个表

映射器可以构造与任意关系单元(称为 selectables)相对应的类,除了普通表之外。例如,join() 函数创建了一个包含多个表的可选择单元,具有自己的复合主键,可以与 Table 相同的方式映射:

from sqlalchemy import Table, Column, Integer, String, MetaData, join, ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import column_property

metadata_obj = MetaData()

# define two Table objects
user_table = Table(
    "user",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String),
)

address_table = Table(
    "address",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("user_id", Integer, ForeignKey("user.id")),
    Column("email_address", String),
)

# define a join between them.  This
# takes place across the user.id and address.user_id
# columns.
user_address_join = join(user_table, address_table)

class Base(DeclarativeBase):
    metadata = metadata_obj

# map to it
class AddressUser(Base):
    __table__ = user_address_join

    id = column_property(user_table.c.id, address_table.c.user_id)
    address_id = address_table.c.id

在上面的示例中,连接表示了 user 表和 address 表的列。user.idaddress.user_id 列通过外键相等,因此在映射中它们被定义为一个属性,即 AddressUser.id,使用 column_property() 来指示一个特殊的列映射。基于这部分配置,当发生 flush 时,映射将把新的主键值从 user.id 复制到 address.user_id 列。

另外,address.id 列被显式映射到一个名为 address_id 的属性。这是为了消除歧义,将 address.id 列的映射与同名的 AddressUser.id 属性区分开来,这里已经被分配为引用 user 表与 address.user_id 外键结合的表。

上面映射的自然主键是 (user.id, address.id) 的组合,因为这些是 useraddress 表的联合主键列。AddressUser 对象的标识将根据这两个值,并且从 AddressUser 对象表示为 (AddressUser.id, AddressUser.address_id)

当涉及到 AddressUser.id 列时,大多数 SQL 表达式将仅使用映射列列表中的第一列,因为这两列是同义的。然而,对于特殊用例,比如 GROUP BY 表达式,在这种情况下需要同时引用两列,并且在使用正确的上下文时,即考虑到别名和类似情况时,可以使用访问器 Comparator.expressions

stmt = select(AddressUser).group_by(*AddressUser.id.expressions)

新功能在版本 1.3.17 中添加:增加了 Comparator.expressions 访问器。

注意

如上所示的针对多个表的映射支持持久化,即对目标表中的行进行 INSERT、UPDATE 和 DELETE。然而,它不支持一次为一个记录在一个表上执行 UPDATE 并在其他表上同时执行 INSERT 或 DELETE 的操作。也就是说,如果一个记录 PtoQ 被映射到“p”和“q”表,其中它基于“p”和“q”的 LEFT OUTER JOIN 有一行,如果进行一个 UPDATE 来修改现有记录中“q”表中的数据,那么“q”中的行必须存在;如果主键标识已经存在,它不会发出 INSERT。如果行不存在,对于大多数支持报告 UPDATE 受影响行数的 DBAPI 驱动程序,ORM 将无法检测到更新的行并引发错误;否则,数据将被静默忽略。

一个允许在相关行上“即时”插入的方法可能会使用.MapperEvents.before_update 事件,并且看起来像:

from sqlalchemy import event

@event.listens_for(PtoQ, "before_update")
def receive_before_update(mapper, connection, target):
    if target.some_required_attr_on_q is None:
        connection.execute(q_table.insert(), {"id": target.id})

在上面的例子中,通过使用Table.insert()创建一个 INSERT 构造,然后使用给定的Connection执行它,将一行 INSERT 到q_table表中,这个 Connection 与用于发出 flush 过程中的其他 SQL 的 Connection 相同。用户提供的逻辑必须检测到从“p”到“q”的 LEFT OUTER JOIN 没有“q”侧的条目。## 对任意子查询映射类

类似于针对连接的映射,也可以将一个普通的select()对象与映射器一起使用。下面的示例片段说明了将一个名为Customer的类映射到一个包含与子查询连接的select()中:

from sqlalchemy import select, func

subq = (
    select(
        func.count(orders.c.id).label("order_count"),
        func.max(orders.c.price).label("highest_order"),
        orders.c.customer_id,
    )
    .group_by(orders.c.customer_id)
    .subquery()
)

customer_select = (
    select(customers, subq)
    .join_from(customers, subq, customers.c.id == subq.c.customer_id)
    .subquery()
)

class Customer(Base):
    __table__ = customer_select

在上面,由customer_select表示的完整行将是customers表的所有列,以及subq子查询暴露的那些列,即order_counthighest_ordercustomer_id。将Customer类映射到这个可选择的内容,然后创建一个包含这些属性的类。

当 ORM 持久化Customer的新实例时,实际上只有customers表会收到 INSERT。这是因为orders表的主键没有在映射中表示;ORM 只会对已经映射了主键的表发出 INSERT。

注意

映射到任意 SELECT 语句的做法,特别是像上面这样复杂的语句,几乎从不需要;它必然倾向于生成复杂的查询,这些查询通常比直接构造查询要低效。这种做法在某种程度上基于 SQLAlchemy 的非常早期历史,其中Mapper构造被认为是主要的查询接口;在现代用法中,Query对象可以用于构造几乎任何 SELECT 语句,包括复杂的复合语句,并且应优先于“映射到可选”的方法。

为一个类映射多个映射器

在现代的 SQLAlchemy 中,一个特定的类一次只能由一个所谓的主要映射器(mapper)映射。这个映射器涉及三个主要功能领域:查询、持久性和对映射类的仪器化。主要映射器的理论基础与以下事实相关:Mapper修改了类本身,不仅将其持久化到特定的Table中,还对类上的属性进行了仪器化,这些属性根据表元数据特别结构化。不能有多个映射器与一个类同等相关,因为只有一个映射器可以实际仪器化该类。

“非主要”映射器的概念已经存在多个 SQLAlchemy 版本,但从 1.3 版本开始,此功能已被弃用。其中一个非主要映射器有用的情况是构建与备用可选择类之间的关系时。现在可以使用aliased构造来满足此用例,并在 Relationship to Aliased Class 中进行了描述。

就一个类可以在不同情境下被完全持久化到不同表中的用例而言,早期版本的 SQLAlchemy 提供了一个来自 Hibernate 的功能,称为“实体名称”功能。然而,在 SQLAlchemy 中,一旦映射类本身成为 SQL 表达式构造的来源,即类的属性直接链接到映射表列,这个用例就变得不可行了。该功能被移除,并被一个简单的面向配方的方法取代,以完成此任务而不产生任何仪器化的歧义——创建新的子类,每个类都被单独映射。该模式现在作为一种配方在Entity Name中提供。

将一个类映射到多个表

Mappers 可以针对任意关系单元(称为selectables)进行构建,而不仅仅是普通的表。例如,join() 函数创建了一个包含多个表的可选单元,其中包括其自己的复合主键,可以与Table 以相同的方式映射:

from sqlalchemy import Table, Column, Integer, String, MetaData, join, ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import column_property

metadata_obj = MetaData()

# define two Table objects
user_table = Table(
    "user",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("name", String),
)

address_table = Table(
    "address",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("user_id", Integer, ForeignKey("user.id")),
    Column("email_address", String),
)

# define a join between them.  This
# takes place across the user.id and address.user_id
# columns.
user_address_join = join(user_table, address_table)

class Base(DeclarativeBase):
    metadata = metadata_obj

# map to it
class AddressUser(Base):
    __table__ = user_address_join

    id = column_property(user_table.c.id, address_table.c.user_id)
    address_id = address_table.c.id

在上面的示例中,连接表示了useraddress表的列。 user.idaddress.user_id列由外键等于,因此在映射中它们被定义为一个属性AddressUser.id,使用column_property()指示专门的列映射。根据配置的这一部分,当发生刷新时,映射将新的主键值从user.id复制到address.user_id列。

另外,address.id列显式映射到名为address_id的属性。这是为了消除歧义,将address.id列的映射与同名的AddressUser.id属性分开,这里已经被分配为引用user表与address.user_id外键的属性。

上述映射的自然主键是(user.id, address.id)的组合,因为这些是useraddress表的主键列合并在一起。 AddressUser对象的标识将根据这两个值,并且从AddressUser对象表示为(AddressUser.id, AddressUser.address_id)

在引用AddressUser.id列时,大多数 SQL 表达式将仅使用映射列列表中的第一列,因为这两列是同义的。但是,对于特殊用例,例如必须同时引用两列的 GROUP BY 表达式,同时考虑到适当的上下文,即适应别名等,可以使用访问器Comparator.expressions

stmt = select(AddressUser).group_by(*AddressUser.id.expressions)

1.3.17 版本中的新内容:添加了Comparator.expressions 访问器。

注意

如上所示的对多个表的映射支持持久性,即对目标表中的行进行 INSERT、UPDATE 和 DELETE 操作。然而,它不支持在一条记录中同时对一个表进行 UPDATE 并在其他表上执行 INSERT 或 DELETE 的操作。也就是说,如果将记录 PtoQ 映射到“p”和“q”表,其中它基于“p”和“q”的 LEFT OUTER JOIN 的行,如果进行更新以更改现有记录中“q”表中的数据,则“q”中的行必须存在;如果主键标识已经存在,它不会发出 INSERT。如果行不存在,对于大多数支持报告 UPDATE 受影响行数的 DBAPI 驱动程序,ORM 将无法检测到更新的行并引发错误;否则,数据将被静默忽略。

允许在“插入”相关行时使用的配方可能利用.MapperEvents.before_update事件,并且看起来像:

from sqlalchemy import event

@event.listens_for(PtoQ, "before_update")
def receive_before_update(mapper, connection, target):
    if target.some_required_attr_on_q is None:
        connection.execute(q_table.insert(), {"id": target.id})

在上述情况下,通过使用Table.insert()创建一个 INSERT 构造将一行插入q_table表,然后使用给定的Connection执行它,这与用于发出刷新过程中的其他 SQL 的相同连接。用户提供的逻辑必须检测从“p”到“q”的 LEFT OUTER JOIN 是否没有“q”方面的条目。

将类映射到任意子查询

类似于对连接进行映射,也可以将一个普通的select()对象与映射器一起使用。下面的示例片段说明了将名为Customer的类映射到包含与子查询连接的select()的过程:

from sqlalchemy import select, func

subq = (
    select(
        func.count(orders.c.id).label("order_count"),
        func.max(orders.c.price).label("highest_order"),
        orders.c.customer_id,
    )
    .group_by(orders.c.customer_id)
    .subquery()
)

customer_select = (
    select(customers, subq)
    .join_from(customers, subq, customers.c.id == subq.c.customer_id)
    .subquery()
)

class Customer(Base):
    __table__ = customer_select

在上面,由customer_select表示的完整行将是customers表的所有列,以及subq子查询暴露的那些列,即order_counthighest_ordercustomer_id。将Customer类映射到这个可选择的类,然后创建一个包含这些属性的类。

当 ORM 持久化Customer的新实例时,实际上只有customers表会收到 INSERT。这是因为orders表的主键没有在映射中表示;ORM 只会对已映射主键的表发出 INSERT。

注意

对任意 SELECT 语句进行映射的实践,特别是上面那种复杂的情况,几乎从不需要;这必然会产生复杂的查询,通常比直接构造的查询效率低。这种做法在某种程度上基于 SQLAlchemy 的早期历史,其中Mapper构造旨在代表主要的查询接口;在现代用法中,Query对象可用于构造几乎任何 SELECT 语句,包括复杂的复合语句,并且应优先使用“映射到可选择”方法。

一个类对应多个映射器

在现代的 SQLAlchemy 中,一个特定的类在任何时候只被一个所谓的主要映射器所映射。这个映射器涉及三个主要功能领域:查询、持久化和对映射类的仪器化。主要映射器的理念与以下事实相关:Mapper不仅修改类本身,而且将其持久化到特定的Table,还会根据表元数据结构化地仪器化类上的属性。不可能有多个映射器与一个类一样平等地关联,因为只有一个映射器实际上可以仪器化这个类。

“非主要”映射器的概念在许多版本的 SQLAlchemy 中一直存在,但自版本 1.3 起,此功能已不建议使用。唯一需要非主要映射器的情况是在构造与另一个可选择的类的关系时。现在,可以使用aliased构造来满足这个用例,并在关系到别名类中进行描述。

就一个类在不同情境下可以完全持久化到不同表的用例而言,SQLAlchemy 的早期版本提供了一个从 Hibernate 改编而来的功能,称为“实体名称”功能。然而,在 SQLAlchemy 中,一旦映射的类本身成为 SQL 表达式构造的源,即类的属性直接链接到映射表的列,这个用例就变得不可行了。该功能被移除,并用一个简单的基于配方的方法来完成这个任务,而不会有任何仪器化的歧义 - 即创建新的子类,每个类都单独映射。这种模式现在作为实体名称的配方可用。

配置版本计数器

原文:docs.sqlalchemy.org/en/20/orm/versioning.html

Mapper支持管理版本 ID 列,它是单个表列,每当对映射表进行UPDATE时,该列会递增或以其他方式更新其值。每次 ORM 发出UPDATEDELETE对行进行操作时,都会检查该值,以确保内存中持有的值与数据库值匹配。

警告

因为版本控制功能依赖于对象的内存记录的比较,所以该功能仅适用于Session.flush()过程,在此过程中 ORM 将单个内存中的行刷新到数据库。当执行多行 UPDATE 或 DELETE 时,该功能不会生效,使用Query.update()Query.delete()方法,因为这些方法仅发出 UPDATE 或 DELETE 语句,但否则无法直接访问受影响行的内容。

此功能的目的是检测两个并发事务在大致相同的时间修改同一行,或者在可能重用上一个事务的数据而不进行刷新的系统中提供对“过时”行的防护(例如,如果使用Sessionexpire_on_commit=False设置,可能会重用上一个事务的数据)。

简单版本计数

跟踪版本的最直接方法是向映射表添加一个整数列,然后在映射器选项中将其设为version_id_col

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_id = mapped_column(Integer, nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {"version_id_col": version_id}

注意

强烈建议version_id列设为 NOT NULL。版本控制功能不支持版本列中的 NULL 值。

在上面的例子中,User映射使用version_id列跟踪整数版本。当首次刷新User类型的对象时,version_id列的值将为“1”。然后,稍后对表的 UPDATE 将始终以类似以下的方式发出:

UPDATE  user  SET  version_id=:version_id,  name=:name
WHERE  user.id  =  :user_id  AND  user.version_id  =  :user_version_id
-- {"name": "new name", "version_id": 2, "user_id": 1, "user_version_id": 1}

上述 UPDATE 语句正在更新不仅与user.id = 1匹配的行,而且还要求user.version_id = 1,其中“1”是我们已知在此对象上使用的最后版本标识符。如果在其他地方的事务独立修改了该行,则此版本 id 将不再匹配,并且 UPDATE 语句将报告没有匹配的行;这是 SQLAlchemy 测试的条件,确保我们的 UPDATE(或 DELETE)语句匹配了恰好一行。如果没有匹配的行,这表明我们的数据版本已过时,并且会引发StaleDataError异常。

自定义版本计数器/类型

可以使用其他类型或计数器来进行版本控制。常见类型包括日期和 GUID。当使用备用类型或计数器方案时,SQLAlchemy 提供了使用version_id_generator参数的钩子,该参数接受一个版本生成可调用对象。此可调用对象会传递当前已知版本的值,并且预期返回后续版本。

例如,如果我们想使用随机生成的 GUID 跟踪User类的版本控制,我们可以这样做(请注意,某些后端支持原生 GUID 类型,但我们在这里使用简单的字符串进行说明):

import uuid

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_uuid = mapped_column(String(32), nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {
        "version_id_col": version_uuid,
        "version_id_generator": lambda version: uuid.uuid4().hex,
    }

每次User对象需要进行 INSERT 或 UPDATE 操作时,持久化引擎将调用uuid.uuid4()。在这种情况下,我们的版本生成函数可以忽略version的传入值,因为uuid4()函数生成的标识符不需要任何先决条件值。如果我们使用的是顺序版本控制方案,例如数字或特殊字符系统,我们可以利用给定的version来帮助确定后续值。

另请参阅

跨后端通用 GUID 类型 ## 服务器端版本计数器

version_id_generator也可以配置为依赖于数据库生成的值。在这种情况下,数据库需要某种方式在行进行 INSERT 时生成新的标识符,以及在 UPDATE 时生成。对于 UPDATE 情况,通常需要一个更新触发器,除非所涉及的数据库支持其他一些本地版本标识符。特别是 PostgreSQL 数据库支持一个称为xmin的系统列,它提供了 UPDATE 版本控制。我们可以如下使用 PostgreSQL 的xmin列为我们的User类版本控制:

from sqlalchemy import FetchedValue

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50), nullable=False)
    xmin = mapped_column("xmin", String, system=True, server_default=FetchedValue())

    __mapper_args__ = {"version_id_col": xmin, "version_id_generator": False}

使用上述映射,ORM 将依赖于xmin列自动提供版本 id 计数器的新值。

当 ORM 发出 INSERT 或 UPDATE 时,通常不会主动获取数据库生成的值,而是将这些列保留为“过期”,在下次访问它们时获取,除非设置了eager_defaults Mapper标志。但是,当使用服务器端版本列时,ORM 需要主动获取新生成的值。这样做是为了在任何并发事务可能再次更新之前设置版本计数器之前。最好同时在 INSERT 或 UPDATE 语句中使用 RETURNING 进行获取,否则,如果之后发出 SELECT 语句,则仍然存在潜在的竞争条件,版本计数器可能在获取之前更改。

当目标数据库支持 RETURNING 时,我们的User类的 INSERT 语句如下所示:

INSERT  INTO  "user"  (name)  VALUES  (%(name)s)  RETURNING  "user".id,  "user".xmin
-- {'name': 'ed'}

在上述情况下,ORM 可以在一条语句中获取任何新生成的主键值以及服务器生成的版本标识符。当后端不支持 RETURNING 时,必须对每个INSERT 和 UPDATE 发出额外的 SELECT,这非常低效,还会引入可能丢失版本计数器的可能性:

INSERT  INTO  "user"  (name)  VALUES  (%(name)s)
-- {'name': 'ed'}

SELECT  "user".version_id  AS  user_version_id  FROM  "user"  where
"user".id  =  :param_1
-- {"param_1": 1}

强烈建议仅在绝对必要时且仅在支持 RETURNING 的后端上使用服务器端版本计数器,目前支持的后端有 PostgreSQL、Oracle、MariaDB 10.5、SQLite 3.35 和 SQL Server。

编程或条件版本计数器

version_id_generator设置为 False 时,我们还可以以与分配任何其他映射属性相同的方式,在对象上编程(和有条件地)设置版本标识符。例如,如果我们使用了 UUID 示例,但将version_id_generator设置为False,我们可以随意设置版本标识符:

import uuid

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_uuid = mapped_column(String(32), nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {"version_id_col": version_uuid, "version_id_generator": False}

u1 = User(name="u1", version_uuid=uuid.uuid4())

session.add(u1)

session.commit()

u1.name = "u2"
u1.version_uuid = uuid.uuid4()

session.commit()

我们也可以在不增加版本计数器的情况下更新我们的User对象;计数器的值将保持不变,并且 UPDATE 语句仍将针对先前的值进行检查。对于仅某些类别的 UPDATE 对并发问题敏感的方案,这可能很有用:

# will leave version_uuid unchanged
u1.name = "u3"
session.commit()

简单版本计数

跟踪版本的最直接方法是向映射表添加一个整数列,然后将其设置为映射选项中的version_id_col

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_id = mapped_column(Integer, nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {"version_id_col": version_id}

注意

强烈建议version_id列设置为 NOT NULL。版本控制功能不支持版本控制列中的 NULL 值。

上面,User映射使用列version_id跟踪整数版本。当首次刷新User类型的对象时,version_id列的值将为“1”。然后,稍后对表的 UPDATE 将始终以类似以下方式发出:

UPDATE  user  SET  version_id=:version_id,  name=:name
WHERE  user.id  =  :user_id  AND  user.version_id  =  :user_version_id
-- {"name": "new name", "version_id": 2, "user_id": 1, "user_version_id": 1}

上述 UPDATE 语句正在更新不仅与 user.id = 1 匹配的行,而且还要求 user.version_id = 1,其中“1”是我们已知的此对象上一次使用的最后版本标识符。如果其他地方的事务独立修改了行,则此版本 ID 将不再匹配,UPDATE 语句将报告没有匹配的行;这是 SQLAlchemy 测试的条件,确保我们的 UPDATE(或 DELETE)语句仅匹配了一行。如果没有匹配的行,则表示我们的数据版本已过期,并且会引发 StaleDataError

自定义版本计数器 / 类型

其他类型的值或计数器可以用于版本控制。常见的类型包括日期和 GUID。当使用替代类型或计数器方案时,SQLAlchemy 提供了一个钩子来使用 version_id_generator 参数,该参数接受版本生成可调用对象。此可调用对象将传递当前已知版本的值,并且预计返回后续版本。

例如,如果我们想要使用随机生成的 GUID 跟踪我们的 User 类的版本控制,我们可以这样做(请注意,一些后端支持原生的 GUID 类型,但我们在这里使用简单的字符串进行演示):

import uuid

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_uuid = mapped_column(String(32), nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {
        "version_id_col": version_uuid,
        "version_id_generator": lambda version: uuid.uuid4().hex,
    }

持久性引擎每次将 User 对象受到 INSERT 或 UPDATE 影响时都会调用 uuid.uuid4()。在这种情况下,我们的版本生成函数可以忽略 version 的传入值,因为 uuid4() 函数生成的标识符没有任何先决条件值。如果我们使用的是顺序版本控制方案,例如数字或特殊字符系统,则可以利用给定的 version 来帮助确定后续值。

另请参阅

不特定后端的 GUID 类型

服务器端版本计数器

version_id_generator 也可以配置为依赖于数据库生成的值。在这种情况下,数据库需要在将行受到 INSERT 时以及 UPDATE 时生成新标识符的某种手段。对于 UPDATE 情况,通常需要一个更新触发器,除非所涉及的数据库支持其他本地版本标识符。特别是,PostgreSQL 数据库支持一个名为 xmin 的系统列,提供 UPDATE 版本控制。我们可以利用 PostgreSQL 的 xmin 列来为我们的 User 类进行版本控制,如下所示:

from sqlalchemy import FetchedValue

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50), nullable=False)
    xmin = mapped_column("xmin", String, system=True, server_default=FetchedValue())

    __mapper_args__ = {"version_id_col": xmin, "version_id_generator": False}

使用上述映射,ORM 将依赖于 xmin 列来自动提供版本 ID 计数器的新值。

当 ORM 发出 INSERT 或 UPDATE 时,通常不会主动获取数据库生成的值的值,而是将这些列保留为“过期”,并在下次访问它们时获取,除非设置了 eager_defaults Mapper 标志。然而,当使用服务器端版本列时,ORM 需要主动获取新生成的值。这是为了在任何并发事务可能再次更新它之前设置版本计数器。最好在 INSERT 或 UPDATE 语句中同时进行这个获取,使用 RETURNING,否则,如果之后发出一个 SELECT 语句,那么版本计数器在它被获取之前可能会发生竞争条件。

当目标数据库支持 RETURNING 时,我们的 User 类的 INSERT 语句将如下所示:

INSERT  INTO  "user"  (name)  VALUES  (%(name)s)  RETURNING  "user".id,  "user".xmin
-- {'name': 'ed'}

在上述情况下,ORM 可以在一个语句中获取任何新生成的主键值以及服务器生成的版本标识符。当后端不支持 RETURNING 时,必须为每个 INSERT 和 UPDATE 发出额外的 SELECT,这非常低效,还会引入遗漏版本计数器的可能性:

INSERT  INTO  "user"  (name)  VALUES  (%(name)s)
-- {'name': 'ed'}

SELECT  "user".version_id  AS  user_version_id  FROM  "user"  where
"user".id  =  :param_1
-- {"param_1": 1}

仅在绝对必要时,并且仅在支持返回的后端上,强烈建议仅使用服务器端版本计数器,目前支持的后端有 PostgreSQL、Oracle、MariaDB 10.5、SQLite 3.35 和 SQL Server。

编程或有条件的版本计数器

version_id_generator 设置为 False 时,我们也可以以编程方式(并有条件地)像分配任何其他映射属性一样,在对象上设置版本标识符。例如,如果我们使用了 UUID 示例,但将 version_id_generator 设置为 False,我们可以根据自己的需要设置版本标识符:

import uuid

class User(Base):
    __tablename__ = "user"

    id = mapped_column(Integer, primary_key=True)
    version_uuid = mapped_column(String(32), nullable=False)
    name = mapped_column(String(50), nullable=False)

    __mapper_args__ = {"version_id_col": version_uuid, "version_id_generator": False}

u1 = User(name="u1", version_uuid=uuid.uuid4())

session.add(u1)

session.commit()

u1.name = "u2"
u1.version_uuid = uuid.uuid4()

session.commit()

我们还可以在不递增版本计数器的情况下更新我们的 User 对象;计数器的值将保持不变,并且 UPDATE 语句仍将根据先前的值进行检查。这在仅特定类的 UPDATE 对并发问题敏感的方案中可能很有用:

# will leave version_uuid unchanged
u1.name = "u3"
session.commit()

类映射 API

原文:docs.sqlalchemy.org/en/20/orm/mapping_api.html

对象名称 描述
add_mapped_attribute(target, key, attr) 向 ORM 映射类添加新的映射属性。
as_declarative(**kw) 类装饰器,将给定的类适配为declarative_base()
class_mapper(class_[, configure]) 给定一个类,返回与该键关联的主要Mapper
clear_mappers() 从所有类中删除所有映射器。
column_property(column, *additional_columns, [group, deferred, raiseload, comparator_factory, init, repr, default, default_factory, compare, kw_only, active_history, expire_on_flush, info, doc]) 为映射提供列级别属性。
configure_mappers() 初始化到目前为止已在所有registry集合中构造的所有映射器之间的相互关系。
declarative_base(*, [metadata, mapper, cls, name, class_registry, type_annotation_map, constructor, metaclass]) 构造用于声明性类定义的基类。
declarative_mixin(cls) 将类标记为提供“声明混入”功能。
DeclarativeBase 用于声明性类定义的基类。
DeclarativeBaseNoMeta DeclarativeBase相同,但不使用元类拦截新属性。
declared_attr 将类级别方法标记为表示映射属性或声明式指令定义的方法。
has_inherited_table(cls) 给定一个类,如果它继承的任何类都有映射表,则返回 True,否则返回 False。
identity_key([class_, ident], *, [instance, row, identity_token]) 生成“标识键”元组,这些元组用作Session.identity_map 字典中的键。
mapped_column([__name_pos, _type_pos], *args, [init, repr, default, default_factory, compare, kw_only, nullable, primary_key, deferred, deferred_group, deferred_raiseload, use_existing_column, name, type, autoincrement, doc, key, index, unique, info, onupdate, insert_default, server_default, server_onupdate, active_history, quote, system, comment, sort_order], **kw) 在 声明式表 配置中声明一个新的 ORM 映射的 Column 构造。
MappedAsDataclass 用于指示映射此类时,同时将其转换为数据类的混合类。
MappedClassProtocol 表示 SQLAlchemy 映射类的协议。
Mapper 定义 Python 类与数据库表或其他关系结构之间的关联,以便对该类进行的 ORM 操作可以继续进行。
object_mapper(instance) 给定一个对象,返回与对象实例关联的主要 Mapper。
orm_insert_sentinel([name, type_], *, [default, omit_from_statements]) 提供一个替代 mapped_column() 的代理,生成所谓的 sentinel 列,允许在其他情况下没有符合条件的主键配置的表中进行高效的批量插入,并且具有确定性的 RETURNING 排序。
polymorphic_union(table_map, typecolname[, aliasname, cast_nulls]) 创建多态映射器使用的 UNION 语句。
reconstructor(fn) 将方法装饰为 ‘reconstructor’ 钩子。
注册表 用于映射类的通用注册表。
synonym_for(name[, map_column]) 与 Python 描述符一起生成一个 synonym() 属性的装饰器。
class sqlalchemy.orm.registry

用于映射类的通用注册表。

registry 用作维护映射集合的基础,并提供用于映射类的配置钩子。

支持的三种常规映射类型是声明基类(Declarative Base)、声明装饰器(Declarative Decorator)和命令式映射(Imperative Mapping)。所有这些映射样式都可以互换使用:

  • registry.generate_base() 返回一个新的声明基类,是 declarative_base() 函数的底层实现。

  • registry.mapped() 提供了一个类装饰器,它将为一个类应用声明性映射,而不使用声明性基类。

  • registry.map_imperatively() 会为一个类生成一个 Mapper,而不会扫描该类以寻找声明性类属性。这种方法适用于历史上由 sqlalchemy.orm.mapper() 传统映射函数提供的用例,该函数已在 SQLAlchemy 2.0 中移除。

从版本 1.4 新增。

成员

init(), as_declarative_base(), configure(), dispose(), generate_base(), map_declaratively(), map_imperatively(), mapped(), mapped_as_dataclass(), mappers, update_type_annotation_map()

参见

ORM 映射类概述 - 类映射样式概述。

method __init__(*, metadata: Optional[MetaData] = None, class_registry: Optional[clsregistry._ClsRegistryType] = None, type_annotation_map: Optional[_TypeAnnotationMapType] = None, constructor: Callable[..., None] = <function _declarative_constructor>)

构建一个新的 registry

参数:

  • metadata – 一个可选的 MetaData 实例。使用声明性表映射生成的所有 Table 对象将使用此 MetaData 集合。如果将此参数保留在默认值 None,则会创建一个空白的 MetaData 集合。

  • constructor – 指定在没有自己的 __init__ 的映射类上的 __init__ 函数的实现。默认情况下,为声明的字段和关系分配 **kwargs 的实现分配给一个实例。如果提供 None,则不会提供 init,并且构造将回退到 cls.init 的普通 Python 语义。

  • class_registry – 可选的字典,当使用字符串名称来标识 relationship() 等内部类时,将充当类名称->映射类的注册表。允许两个或更多声明性基类共享相同的类名称注册表,以简化基类之间的关系。

  • type_annotation_map

    可选的 Python 类型到 SQLAlchemy TypeEngine类或实例的字典。提供的字典将更新默认类型映射。这仅由MappedColumn构造在Mapped类型内部的注解产生列类型时使用。

    新版本 2.0 中的内容。

    另请参阅

    自定义类型映射

method as_declarative_base(**kw: Any) → Callable[[Type[_T]], Type[_T]]

类装饰器,将为给定的基类调用registry.generate_base()

例如:

from sqlalchemy.orm import registry

mapper_registry = registry()

@mapper_registry.as_declarative_base()
class Base:
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
    id = Column(Integer, primary_key=True)

class MyMappedClass(Base):
    # ...

所有传递给registry.as_declarative_base()的关键字参数都会传递给registry.generate_base()

method configure(cascade: bool = False) → None

配置此注册表中所有尚未配置的映射器。

配置步骤用于调和和初始化relationship()链接,以及调用配置事件,如MapperEvents.before_configured()MapperEvents.after_configured(),这些事件可以被 ORM 扩展或用户定义的扩展钩子所使用。

如果此注册表中的一个或多个映射器包含指向其他注册表中映射类的relationship()构造,则称该注册表为依赖于那些注册表。为了自动配置这些依赖注册表,configure.cascade标志应设置为True。否则,如果它们未配置,则会引发异常。此行为背后的原理是允许应用程序在控制是否隐式到达其他注册表的同时,以编程方式调用注册表的配置。

作为调用registry.configure()的替代方案,可以使用 ORM 函数configure_mappers()函数确保内存中所有registry对象的配置完成。这通常更简单,并且还早于整体使用registry对象的用法。但是,此函数将影响运行中的 Python 进程中的所有映射,并且对于具有许多用于不同目的的注册表的应用程序可能更耗费内存/时间,这些注册表可能不会立即需要。

另请参阅

configure_mappers()

自版本 1.4.0b2 新增。

method dispose(cascade: bool = False) → None

处理此 registry 中的所有映射器。

调用后,此注册表中映射的所有类将不再具有与类相关联的类仪器。该方法是每个registry的类似于应用程序范围的clear_mappers()函数。

如果此注册表包含其他注册表的依赖项映射器,通常通过relationship()链接,则必须将这些注册表也处理掉。当这些注册表存在于与此相关的关系中时,如果设置了dispose.cascade标志为True,则它们的registry.dispose()方法也将被调用;否则,如果这些注册表尚未被处理,则会引发错误。

自版本 1.4.0b2 新增。

另请参阅

clear_mappers()

method generate_base(mapper: ~typing.Callable[[...], ~sqlalchemy.orm.mapper.Mapper[~typing.Any]] | None = None, cls: ~typing.Type[~typing.Any] = <class 'object'>, name: str = 'Base', metaclass: ~typing.Type[~typing.Any] = <class 'sqlalchemy.orm.decl_api.DeclarativeMeta'>) → Any

生成一个声明性基类。

继承自返回的类对象的类将使用声明性映射自动映射。

例如:

from sqlalchemy.orm import registry

mapper_registry = registry()

Base = mapper_registry.generate_base()

class MyClass(Base):
    __tablename__ = "my_table"
    id = Column(Integer, primary_key=True)

上述动态生成的类等同于下面的非动态示例:

from sqlalchemy.orm import registry
from sqlalchemy.orm.decl_api import DeclarativeMeta

mapper_registry = registry()

class Base(metaclass=DeclarativeMeta):
    __abstract__ = True
    registry = mapper_registry
    metadata = mapper_registry.metadata

    __init__ = mapper_registry.constructor

自版本 2.0 变更:请注意,registry.generate_base()方法已被新的DeclarativeBase类取代,该类使用子类化生成一个新的“基”类,而不是函数的返回值。这样可以与PEP 484类型工具兼容的方法。

registry.generate_base()方法提供了declarative_base()函数的实现,该函数一次性创建了registry和基类。

查看声明式映射部分以获取背景和示例。

参数:

  • mapper – 可选可调用对象,默认为Mapper。此函数用于生成新的Mapper对象。

  • cls – 默认为object。要用作生成的声明性基类的基础的类型。可以是类或类的元组。

  • name – 默认为Base。生成类的显示名称。虽然不需要自定义此项,但可以提高回溯和调试时的清晰度。

  • metaclass – 默认为DeclarativeMeta。作为生成的声明性基类的元类型的元类或__metaclass__兼容可调用对象。

另请参阅

声明式映射

declarative_base()

method map_declaratively(cls: Type[_O]) → Mapper[_O]

声明性地映射一个类。

在这种映射形式中,将扫描类以获取映射信息,包括要与表关联的列和/或实际表对象。

返回Mapper对象。

例如:

from sqlalchemy.orm import registry

mapper_registry = registry()

class Foo:
    __tablename__ = 'some_table'

    id = Column(Integer, primary_key=True)
    name = Column(String)

mapper = mapper_registry.map_declaratively(Foo)

此函数更方便地通过registry.mapped()类装饰器或通过从registry.generate_base()生成的声明性元类的子类间接调用。

查看完整详情和示例,请参阅声明式映射部分。

参数:

cls – 要映射的类。

返回:

一个Mapper对象。

另请参阅

声明式映射

registry.mapped() - 更常见的此函数的装饰器接口。

registry.map_imperatively()

method map_imperatively(class_: Type[_O], local_table: FromClause | None = None, **kw: Any) → Mapper[_O]

命令式地映射一个类。

在这种映射形式中,不会扫描类以获取任何映射信息。相反,所有映射构造都作为参数传递。

此方法旨在与现在已删除的 SQLAlchemy mapper()函数完全等效,只是以特定注册表的术语表示。

例如:

from sqlalchemy.orm import registry

mapper_registry = registry()

my_table = Table(
    "my_table",
    mapper_registry.metadata,
    Column('id', Integer, primary_key=True)
)

class MyClass:
    pass

mapper_registry.map_imperatively(MyClass, my_table)

查看完整背景和用法示例,请参阅命令式映射部分。

参数:

  • class_ – 要映射的类。对应于Mapper.class_参数。

  • local_table – 映射的主题是Table或其他FromClause对象。对应于Mapper.local_table参数。

  • **kw – 所有其他关键字参数直接传递给Mapper构造函数。

另请参见

命令式映射

声明式映射

method mapped(cls: Type[_O]) → Type[_O]

类装饰器,将声明式映射过程应用于给定的类。

例如:

from sqlalchemy.orm import registry

mapper_registry = registry()

@mapper_registry.mapped
class Foo:
    __tablename__ = 'some_table'

    id = Column(Integer, primary_key=True)
    name = Column(String)

参见声明式映射部分,获取完整的细节和示例。

参数:

cls – 要映射的类。

返回:

传递的类。

另请参见

声明式映射

registry.generate_base() - 生成一个基类,将自动对子类应用声明式映射,使用 Python 元类。

另请参见

registry.mapped_as_dataclass()

method mapped_as_dataclass(_registry__cls: Type[_O] | None = None, *, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, eq: _NoArg | bool = _NoArg.NO_ARG, order: _NoArg | bool = _NoArg.NO_ARG, unsafe_hash: _NoArg | bool = _NoArg.NO_ARG, match_args: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, dataclass_callable: _NoArg | Callable[..., Type[Any]] = _NoArg.NO_ARG) → Type[_O] | Callable[[Type[_O]], Type[_O]]

类装饰器,将声明式映射过程应用于给定的类,并将类转换为 Python 数据类。

另请参见

声明式数据类映射 - SQLAlchemy 原生数据类映射的完整背景

版本 2.0 中的新功能。

attribute mappers

所有Mapper对象的只读集合。

method update_type_annotation_map(type_annotation_map: _TypeAnnotationMapType) → None

使用新值更新registry.type_annotation_map

function sqlalchemy.orm.add_mapped_attribute(target: Type[_O], key: str, attr: MapperProperty[Any]) → None

向 ORM 映射类添加新的映射属性。

例如:

add_mapped_attribute(User, "addresses", relationship(Address))

这可用于未使用截获属性设置操作的声明性元类的 ORM 映射。

版本 2.0 中的新功能。

function sqlalchemy.orm.column_property(column: _ORMColumnExprArgument[_T], *additional_columns: _ORMColumnExprArgument[Any], group: str | None = None, deferred: bool = False, raiseload: bool = False, comparator_factory: Type[PropComparator[_T]] | None = None, 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, active_history: bool = False, expire_on_flush: bool = True, info: _InfoType | None = None, doc: str | None = None) → MappedSQLExpression[_T]

为映射提供列级属性。

使用声明式映射时,column_property()用于将只读的 SQL 表达式映射到映射类。

使用命令式映射时,column_property()还承担了将表列与附加功能进行映射的角色。使用完全声明式映射时,应使用mapped_column()构造来实现此目的。

在声明式数据类映射中,column_property() 被认为是只读的,并且不会包含在数据类的 __init__() 构造函数中。

column_property() 函数返回 ColumnProperty 的实例。

另请参阅

使用 column_property - 通常使用 column_property() 来映射 SQL 表达式。

对命令式表列应用加载、持久化和映射选项 - 使用column_property()与命令式表映射,将附加选项应用到普通Column对象的用法。

参数:

  • *cols – 要映射的列对象列表。

  • active_history=False – 仅用于命令式表映射,或遗留式声明式映射(即尚未升级为mapped_column()的映射),用于预期可写的基于列的属性;对于声明式映射,请使用 mapped_column()mapped_column.active_history。有关功能细节,请参阅该参数。

  • comparator_factory – 一个继承自Comparator的类,提供比较操作的自定义 SQL 子句生成。

  • group – 当标记为延迟加载时,此属性的组名称。

  • deferred – 当为 True 时,列属性是“延迟加载”的,这意味着它不会立即加载,而是在首次在实例上访问属性时加载。另请参阅 deferred()

  • doc – 可选字符串,将应用于类绑定的描述符的文档。

  • expire_on_flush=True – 禁用刷新时的过期。引用 SQL 表达式(而不是单个表绑定列)的 column_property() 被视为“只读”属性;填充它对数据状态没有影响,它只能返回数据库状态。因此,每当父对象涉及到刷新时,即在刷新中具有任何类型的“脏”状态时,都会过期 column_property() 的值。将此参数设置为 False 将导致在刷新继续进行后保留任何现有值。请注意,默认过期设置的 Session 仍在 Session.commit() 调用后过期所有属性,但是。

  • info – 可选数据字典,将填充到此对象的 MapperProperty.info 属性中。

  • raiseload

    如果为 True,则表示在未延迟加载列时应引发错误,而不是加载值。可以通过在查询时使用带有 raiseload=False 的 deferred() 选项来更改此行为。

    从版本 1.4 开始新增。

    另请参阅

    使用 raiseload 避免延迟列加载

  • init

    自版本 1.4 起弃用:column_property.init 参数对于 column_property() 已弃用。此参数仅适用于声明性数据类配置中的可写属性,而 column_property() 在此上下文中被视为只读属性。

  • default

    自版本 1.4 起弃用:column_property.default 参数对于 column_property() 已弃用。此参数仅适用于声明性数据类配置中的可写属性,而 column_property() 在此上下文中被视为只读属性。

  • default_factory

    自 1.4 版本起弃用:column_property.default_factory 参数已弃用于 column_property()。此参数仅适用于声明性数据类配置中的可写属性,而在此上下文中,column_property() 被视为只读属性。

  • kw_only

    自 1.4 版本起弃用:column_property.kw_only 参数已弃用于 column_property()。此参数仅适用于声明性数据类配置中的可写属性,而在此上下文中,column_property() 被视为只读属性。

function sqlalchemy.orm.declarative_base(*, metadata: Optional[MetaData] = None, mapper: Optional[Callable[..., Mapper[Any]]] = None, cls: Type[Any] = <class 'object'>, name: str = 'Base', class_registry: Optional[clsregistry._ClsRegistryType] = None, type_annotation_map: Optional[_TypeAnnotationMapType] = None, constructor: Callable[..., None] = <function _declarative_constructor>, metaclass: Type[Any] = <class 'sqlalchemy.orm.decl_api.DeclarativeMeta'>) → Any

构建用于声明性类定义的基类。

新的基类将被赋予一个元类,该元类生成适当的 Table 对象,并根据在类及其任何子类中声明的信息进行适当的 Mapper 调用。

在 2.0 版本中更改:注意 declarative_base() 函数已被新的 DeclarativeBase 类所取代,该类使用子类化生成一个新的“基”类,而不是一个函数的返回值。这允许与 PEP 484 类型工具兼容的方法。

declarative_base() 函数是使用 registry.generate_base() 方法的简写版本。即:

from sqlalchemy.orm import declarative_base

Base = declarative_base()

等同于:

from sqlalchemy.orm import registry

mapper_registry = registry()
Base = mapper_registry.generate_base()

查看 registryregistry.generate_base() 的文档字符串以获取更多细节。

在 1.4 版本中更改:declarative_base() 函数现在是更通用的 registry 类的特化版本。该函数还从 declarative.ext 包移动到 sqlalchemy.orm 包中。

参数:

  • metadata – 可选的MetaData实例。所有基类的子类隐式声明的所有Table对象将共享此 MetaData。如果未提供,则将创建一个 MetaData 实例。 MetaData 实例将通过生成的声明性基类的 metadata 属性可用。

  • mapper – 可选可调用项,默认为Mapper。将用于将子类映射到其表格。

  • cls – 默认为object。要用作生成的声明性基类的基类的类型。可以是一个类或类的元组。

  • name – 默认为Base。生成类的显示名称。不需要自定义此选项,但可以提高回溯和调试时的清晰度。

  • constructor – 指定在没有自己的__init__的映射类上实现__init__函数的实现。默认为一种实现,将声明的字段和关系的 **kwargs 分配给一个实例。如果提供了None,则不会提供__init__,并且构造将通过普通的 Python 语义回退到 cls.__init__

  • class_registry – 可选字典,将用作当使用字符串名称标识relationship()等内部的类时,类名->映射类的注册表。允许两个或更多声明性基类共享相同的类名注册表,以简化基类之间的关系。

  • type_annotation_map

    Python 类型到 SQLAlchemy TypeEngine 类或实例的可选字典。这仅由MappedColumn构造用于基于Mapped类型中的注释生成列类型。

    版本 2.0 中的新功能。

    另请参见

    自定义类型映射

  • metaclass – 默认为DeclarativeMeta。要用作生成的声明性基类的元类型的元类或__metaclass__兼容可调用项。

另请参见

registry

function sqlalchemy.orm.declarative_mixin(cls: Type[_T]) → Type[_T]

将类标记为提供“声明性混合”的功能。

例如:

from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import declarative_mixin

@declarative_mixin
class MyMixin:

    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

    __table_args__ = {'mysql_engine': 'InnoDB'}
    __mapper_args__= {'always_refresh': True}

    id =  Column(Integer, primary_key=True)

class MyModel(MyMixin, Base):
    name = Column(String(1000))

declarative_mixin() 装饰器当前不会以任何方式修改给定的类;其当前目的严格来说是帮助 Mypy 插件能够在没有其他上下文的情况下识别 SQLAlchemy 声明性混合类。

版本 1.4.6 中的新功能。

另请参阅

使用 Mixins 组合映射层次结构

使用 @declared_attr 和声明性 Mixins - 在 Mypy 插件文档中

function sqlalchemy.orm.as_declarative(**kw: Any) → Callable[[Type[_T]], Type[_T]]

类装饰器,将给定的类适应为declarative_base()

此函数利用了registry.as_declarative_base()方法,首先自动创建一个registry,然后调用装饰器。

例如:

from sqlalchemy.orm import as_declarative

@as_declarative()
class Base:
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()
    id = Column(Integer, primary_key=True)

class MyMappedClass(Base):
    # ...

另请参阅

registry.as_declarative_base()

function sqlalchemy.orm.mapped_column(__name_pos: str | _TypeEngineArgument[Any] | SchemaEventTarget | None = None, __type_pos: _TypeEngineArgument[Any] | SchemaEventTarget | None = None, *args: SchemaEventTarget, 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, nullable: bool | Literal[SchemaConst.NULL_UNSPECIFIED] | None = SchemaConst.NULL_UNSPECIFIED, primary_key: bool | None = False, deferred: _NoArg | bool = _NoArg.NO_ARG, deferred_group: str | None = None, deferred_raiseload: bool | None = None, use_existing_column: bool = False, name: str | None = None, type_: _TypeEngineArgument[Any] | None = None, autoincrement: _AutoIncrementType = 'auto', doc: str | None = None, key: str | None = None, index: bool | None = None, unique: bool | None = None, info: _InfoType | None = None, onupdate: Any | None = None, insert_default: Any | None = _NoArg.NO_ARG, server_default: _ServerDefaultArgument | None = None, server_onupdate: FetchedValue | None = None, active_history: bool = False, quote: bool | None = None, system: bool = False, comment: str | None = None, sort_order: _NoArg | int = _NoArg.NO_ARG, **kw: Any) → MappedColumn[Any]

为在声明性表配置中使用的新的 ORM 映射的Column构造声明。

mapped_column()函数提供了一个与 ORM 兼容且与 Python 类型兼容的构造,用于声明性映射,指示映射到 Core Column 对象的属性。当使用声明性时,特别是在使用声明性表配置时,它提供了将属性映射到Column对象的等效功能。

2.0 版中的新功能。

mapped_column()通常与显式类型一起使用,以及Mapped注释类型一起使用,它可以根据Mapped注释中的内容推导出列的 SQL 类型和可空性。它也可以在不带注释的情况下使用,作为 SQLAlchemy 1.x 风格中声明性映射中使用Column的替代品。

对于mapped_column()的使用示例,请参阅使用 mapped_column() 的声明性表中的文档。

另请参阅

使用 mapped_column() 的声明性表 - 完整文档

ORM 声明性模型 - 使用 1.x 风格映射的声明性映射的迁移说明

参数:

  • __name – 要为 Column 指定的字符串名称。这是一个可选的仅位置参数,如果存在,必须是传递的第一个位置参数。如果省略,则将使用 mapped_column() 映射到的属性名称作为 SQL 列名。

  • __typeTypeEngine 类型或实例,指示与 Column 关联的数据类型。这是一个可选的仅位置参数,如果存在,则必须紧随 __name 参数,否则必须是第一个位置参数。如果省略,则列的最终类型可以从注释类型中推导出,或者如果存在 ForeignKey,则可以从引用列的数据类型中推导出。

  • *args – 额外的位置参数包括诸如 ForeignKeyCheckConstraintIdentity 这样的结构,它们被传递到构造的 Column 中。

  • nullable – 可选布尔值,指示列是否应为“NULL”或“NOT NULL”。如果省略,nullability 将根据类型注释推导而来,根据 typing.Optional 是否存在而定。否则,对于非主键列,nullable 默认为 True,对于主键列,默认为 False

  • primary_key – 可选布尔值,表示 Column 是否将成为表的主键。

  • deferred

    可选布尔值 - 此关键字参数由 ORM 声明过程使用,并不是 Column 本身的一部分;相反,它表示此列应当被“延迟”加载,就好像被 deferred() 映射一样。

    另请参阅

    配置映射中的列延迟

  • deferred_group

    暗示将 mapped_column.deferred 设置为 True,并设置 deferred.group 参数。

    另请参阅

    以组加载延迟列

  • deferred_raiseload

    意味着将 mapped_column.deferred 设置为 True,并设置 deferred.raiseload 参数。

    另请参阅

    使用 raiseload 避免延迟加载列

  • use_existing_column

    如果为 True,则尝试在继承的超类(通常是单一继承的超类)上定位给定列名,如果存在,则不会生成新列,将映射到超类列,就好像该列从此类中省略一样。这用于将新列添加到继承的超类的混合类。

    另请参阅

    使用 use_existing_column 解决列冲突

    从 2.0.0b4 版开始新增。

  • default

    如果 mapped_column.insert_default 参数不存在,则直接传递给 Column.default 参数。此外,在使用声明式数据类映射时,表示应该应用于生成的 __init__() 方法内的关键字构造函数的默认 Python 值。

    请注意,在生成数据类时,当 mapped_column.insert_default 不存在时,这意味着 mapped_column.default 的值将在 两个 地方使用,即 __init__() 方法和 Column.default 参数。虽然此行为可能在将来的版本中更改,但目前这种情况通常“可以解决”;None 的默认值意味着 Column 不会得到默认生成器,而引用非None的默认值将在调用__init__()时提前分配给对象,在任何情况下,核心 Insert 构造将使用相同的值,从而导致相同的最终结果。

    注意

    当使用在 Core 级别的列默认值作为可调用对象,由底层Column与 ORM 映射的数据类,特别是那些是上下文感知的默认函数时,必须使用mapped_column.insert_default参数。这是必要的,以消除可调用对象被解释为数据类级别默认值的歧义。

  • insert_default – 直接传递给Column.default参数;当存在时,将取代mapped_column.default的值,但无论何时,mapped_column.default都将应用于数据类映射的构造函数默认值。

  • sort_order

    表示当 ORM 创建Table时,此映射列应如何与其他列排序的整数。对于具有相同值的映射列,默认使用默认排序,首先放置在主类中定义的映射列,然后放置在超类中的映射列。默认为 0。排序为升序。

    版本 2.0.4 中的新内容。

  • active_history=False

    True时,表示应在替换时加载标量属性的“上一个”值,如果尚未加载。通常,简单非主键标量值的历史跟踪逻辑只需要知道“新”值就能执行刷新。此标志适用于需要使用get_history()Session.is_modified()并且还需要知道属性的“上一个”值的应用程序。

    版本 2.0.10 中的新内容。

  • init – 特定于声明性数据类映射,指定映射属性是否应作为数据类过程生成的__init__()方法的一部分。

  • repr – 特定于声明性数据类映射,指定映射属性是否应作为数据类过程生成的__repr__()方法的一部分。

  • default_factory – 特定于声明性数据类映射,指定作为数据类过程生成的__init__()方法的一部分的默认值生成函数。

  • compare

    特定于声明式数据类映射,指示在为映射类生成__eq__()__ne__()方法时,是否应包含此字段在比较操作中。

    在版本 2.0.0b4 中新增。

  • kw_only – 特定于声明式数据类映射,指示在生成__init__()时,是否应将此字段标记为仅关键字。

  • **kw – 所有剩余的关键字参数都传递给Column的构造函数。

class sqlalchemy.orm.declared_attr

将类级方法标记为表示映射属性或声明性指令定义。

declared_attr通常作为类级方法的装饰器应用,将属性转换为类似标量的属性,可以从未实例化的类中调用。声明性映射过程在扫描类时寻找这些declared_attr可调用对象,并假定任何标记为declared_attr的属性将是一个可调用对象,将生成特定于声明性映射或表配置的对象。

declared_attr通常适用于混入类,用于定义应用于类的不同实现者的关系。它还可以用于定义动态生成的列表达式和其他声明性属性。

示例:

class ProvidesUserMixin:
    "A mixin that adds a 'user' relationship to classes."

    user_id: Mapped[int] = mapped_column(ForeignKey("user_table.id"))

    @declared_attr
    def user(cls) -> Mapped["User"]:
        return relationship("User")

当与__tablename__等声明性指令一起使用时,可以使用declared_attr.directive()修饰符,指示PEP 484类型工具,给定的方法不涉及Mapped属性:

class CreateTableName:
    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

declared_attr也可以直接应用于映射类,以允许在使用映射继承方案时,属性可以在子类上动态配置自身。下面说明了使用declared_attr创建为子类生成Mapper.polymorphic_identity参数的动态方案:

class Employee(Base):
    __tablename__ = 'employee'

    id: Mapped[int] = mapped_column(primary_key=True)
    type: Mapped[str] = mapped_column(String(50))

    @declared_attr.directive
    def __mapper_args__(cls) -> Dict[str, Any]:
        if cls.__name__ == 'Employee':
            return {
                    "polymorphic_on":cls.type,
                    "polymorphic_identity":"Employee"
            }
        else:
            return {"polymorphic_identity":cls.__name__}

class Engineer(Employee):
    pass

declared_attr支持装饰使用@classmethod显式装饰的函数。从运行时的角度来看,这从未必要,但可能需要支持不认识已装饰函数具有类级行为的cls参数的PEP 484类型工具:

class SomethingMixin:
    x: Mapped[int]
    y: Mapped[int]

    @declared_attr
    @classmethod
    def x_plus_y(cls) -> Mapped[int]:
        return column_property(cls.x + cls.y)

版本 2.0 中的新功能:- declared_attr可以容纳使用@classmethod装饰的函数,以帮助需要的PEP 484集成。

另见

通过混合组合映射层次结构 - 附带对declared_attr使用模式的背景说明的声明性混合文档。

成员

级联,指令

类签名

sqlalchemy.orm.declared_attr (sqlalchemy.orm.base._MappedAttribute, sqlalchemy.orm.decl_api._declared_attr_common)

attribute cascading

declared_attr标记为级联。

这是一个特殊用途的修饰符,表明在映射继承场景中,列或基于 MapperProperty 的声明属性应该在映射的子类中独立配置。

警告

declared_attr.cascading修饰符有几个限制:

  • 标志only适用于在声明性混合类和__abstract__类上使用declared_attr;当直接在映射类上使用时,它目前没有任何效果。

  • 标志only仅适用于通常命名的属性,例如不是任何特殊下划线属性,例如__tablename__。在这些属性上它没有效果。

  • 当前标志不允许进一步覆盖类层次结构下游;如果子类尝试覆盖属性,则会发出警告并跳过覆盖的属性。这是一个希望在某些时候解决的限制。

下面,无论是MyClass还是MySubClass都将建立一个独特的id列对象:

class HasIdMixin:
    @declared_attr.cascading
    def id(cls):
        if has_inherited_table(cls):
            return Column(ForeignKey("myclass.id"), primary_key=True)
        else:
            return Column(Integer, primary_key=True)

class MyClass(HasIdMixin, Base):
    __tablename__ = "myclass"
    # ...

class MySubClass(MyClass):
  """ """

    # ...

上述配置的行为是,MySubClass将引用其自己的id列以及MyClass下面命名为some_id的属性。

另见

声明性继承

使用 _orm.declared_attr() 生成特定表继承列

attribute directive

declared_attr 标记为装饰声明性指令,如 __tablename____mapper_args__

declared_attr.directive 的目的严格是支持PEP 484类型工具,允许装饰的函数具有不使用 Mapped 通用类的返回类型,这在使用 declared_attr 用于列和映射属性时通常是不会发生的。在运行时,declared_attr.directive 返回未经修改的 declared_attr 类。

例如:

class CreateTableName:
    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

2.0 版本中的新功能。

另请参见

使用 Mixins 组合映射层次结构

declared_attr

class sqlalchemy.orm.DeclarativeBase

用于声明性类定义的基类。

DeclarativeBase 允许以与类型检查器兼容的方式创建新的声明性基类:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

上述 Base 类现在可用作新声明性映射的基类。超类利用 __init_subclass__() 方法设置新类,而不使用元类。

首次使用时,DeclarativeBase 类实例化一个新的 registry 用于与基类一起使用,假设未明确提供。DeclarativeBase 类支持类级属性,这些属性充当此注册表构建的参数;例如指示特定的 MetaData 集合以及 registry.type_annotation_map 的特定值:

from typing_extensions import Annotated

from sqlalchemy import BigInteger
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase

bigint = Annotated[int, "bigint"]
my_metadata = MetaData()

class Base(DeclarativeBase):
    metadata = my_metadata
    type_annotation_map = {
        str: String().with_variant(String(255), "mysql", "mariadb"),
        bigint: BigInteger()
    }

可指定的类级属性包括:

参数:

  • metadata – 可选的MetaData 集合。如果自动构造了一个registry,则将使用该MetaData 集合来构造它。否则,本地的MetaData 集合将取代通过DeclarativeBase.registry 参数传递的现有registry 使用的集合。

  • type_annotation_map – 可选的类型注释映射,将传递给registry 作为registry.type_annotation_map.

  • registry – 直接提供预先存在的registry

2.0 版本中的新功能:添加了DeclarativeBase,以便可以以也被PEP 484类型检查器识别的方式构造声明性基类。因此,DeclarativeBase 和其他基于子类化的 API 应被视为取代先前的“由函数返回的类” API,即declarative_base()registry.generate_base(),其中返回的基类不能被类型检查器识别,除非使用插件。

init 行为

在普通的 Python 类中,类层次结构中基本的 __init__() 方法是 object.__init__(),不接受任何参数。然而,当首次声明DeclarativeBase子类时,如果没有已经存在的 __init__() 方法,该类将被赋予一个 __init__() 方法,该方法链接到registry.constructor 构造函数;这是通常的声明性构造函数,将关键字参数分配给实例的属性,假定这些属性在类级别已经建立(即已映射,或者与描述符链接)。这个构造函数永远不会被映射类直接访问,除非通过显式调用 super(),因为映射类本身会直接得到一个 __init__() 方法,该方法调用registry.constructor,所以在默认情况下独立于基本的 __init__() 方法的操作。

从版本 2.0.1 开始发生了变化:DeclarativeBase 现在具有默认构造函数,默认链接到 registry.constructor,以便调用 super().__init__() 可以访问此构造函数。先前,由于一个实现错误,这个默认构造函数丢失了,调用 super().__init__() 将会调用 object.__init__()

DeclarativeBase 子类也可以声明一个显式的 __init__() 方法,该方法将在此级别替代 registry.constructor 函数的使用:

class Base(DeclarativeBase):
    def __init__(self, id=None):
        self.id = id

映射的类仍然不会隐式调用这个构造函数;只能通过调用 super().__init__() 来访问它:

class MyClass(Base):
    def __init__(self, id=None, name=None):
        self.name = name
        super().__init__(id=id)

请注意,这与诸如传统的 declarative_base() 等函数的行为不同;由这些函数创建的基类将始终为 __init__() 安装 registry.constructor

成员

mapper, mapper_args, table, table_args, tablename, metadata, registry

类签名

sqlalchemy.orm.DeclarativeBase (sqlalchemy.inspection.Inspectable)

attribute __mapper__: ClassVar[Mapper[Any]]

将特定类映射到的 Mapper 对象。

也可以使用 inspect() 获取,例如 inspect(klass)

attribute __mapper_args__: Any

传递给 Mapper 构造函数的参数字典。

另见

使用声明式的 Mapper 配置选项

attribute __table__: ClassVar[FromClause]

将特定子类映射到的 FromClause

这通常是 Table 的一个实例,但根据类的映射方式,也可能是其他类型的 FromClause,比如 Subquery

另见

访问表和元数据

attribute __table_args__: Any

将传递给Table构造函数的参数字典或元组。有关此集合特定结构的背景,请参阅声明式表配置。

另请参阅

声明式表配置

attribute __tablename__: Any

将生成的Table对象分配的字符串名称,如果没有通过DeclarativeBase.__table__直接指定。

另请参阅

使用 mapped_column()的声明式表

attribute metadata: ClassVar[MetaData]

指的是将用于新Table对象的MetaData集合。

另请参阅

访问表和元数据

attribute registry: ClassVar[registry]

指的是新Mapper对象将关联的正在使用的registry

class sqlalchemy.orm.DeclarativeBaseNoMeta

DeclarativeBase相同,但不使用元类拦截新属性。

当希望使用自定义元类时,可以使用DeclarativeBaseNoMeta基类。

2.0 版本中的新功能。

成员

mapper, mapper_args, table, table_args, tablename, metadata, registry

类签名

sqlalchemy.orm.DeclarativeBaseNoMeta (sqlalchemy.inspection.Inspectable)

attribute __mapper__: ClassVar[Mapper[Any]]

将特定类映射到的Mapper对象。

也可以使用inspect()获得,例如inspect(klass)

attribute __mapper_args__: Any

将传递给Mapper构造函数的参数字典。

另请参阅

使用声明式的 Mapper 配置选项

attribute __table__: FromClause | None

将特定子类映射到的FromClause

这通常是Table的实例,但根据类的映射方式,也可能引用其他类型的FromClause,例如Subquery

另请参阅

访问表和元数据

attribute __table_args__: Any

将传递给Table构造函数的参数字典或元组。有关此集合特定结构的背景,请参阅声明式表配置。

另请参阅

声明式表配置

attribute __tablename__: Any

分配给生成的Table对象的字符串名称,如果未直接通过DeclarativeBase.__table__指定。

另请参阅

具有 mapped_column()的声明式表

attribute metadata: ClassVar[MetaData]

指的是将用于新Table对象的MetaData集合。

另请参阅

访问表和元数据

attribute registry: ClassVar[registry]

指的是新的Mapper对象将与之关联的正在使用的registry

function sqlalchemy.orm.has_inherited_table(cls: Type[_O]) → bool

给定一个类,如果它继承的任何类都有一个映射表,则返回 True,否则返回 False。

这在声明式混合中用于构建在继承层次结构中的基类和子类之间行为不同的属性。

另请参阅

使用混合和基类进行映射继承模式

function sqlalchemy.orm.synonym_for(name: str, map_column: bool = False) → Callable[[Callable[[...], Any]], Synonym[Any]]

在与 Python 描述符一起生成synonym()属性的装饰器。

被装饰的函数将被传递给synonym()作为synonym.descriptor参数:

class MyClass(Base):
    __tablename__ = 'my_table'

    id = Column(Integer, primary_key=True)
    _job_status = Column("job_status", String(50))

    @synonym_for("job_status")
    @property
    def job_status(self):
        return "Status: %s" % self._job_status

SQLAlchemy 的混合属性功能通常比同义词更受青睐,后者是一个更传统的功能。

另请参阅

同义词 - 同义词概述

synonym() - 映射器级函数

使用描述符和混合属性 - Hybrid Attribute 扩展提供了一种更新的方法来更灵活地增强属性行为,比使用同义词更容易实现。

function sqlalchemy.orm.object_mapper(instance: _T) → Mapper[_T]

给定一个对象,返回与该对象实例关联的主要映射器。

如果没有配置映射,则引发sqlalchemy.orm.exc.UnmappedInstanceError

此功能可通过检查系统使用:

inspect(instance).mapper

如果实例不是映射的一部分,则使用检查系统将引发sqlalchemy.exc.NoInspectionAvailable

function sqlalchemy.orm.class_mapper(class_: Type[_O], configure: bool = True) → Mapper[_O]

给定一个类,返回与该键关联的主要Mapper

如果给定类上没有配置映射,则引发UnmappedClassError,或者如果传递了非类对象,则引发ArgumentError

相当的功能可以通过inspect()函数实现:

inspect(some_mapped_class)

如果类未映射,则使用检查系统将引发sqlalchemy.exc.NoInspectionAvailable

function sqlalchemy.orm.configure_mappers() → None

初始化到目前为止在所有registry集合中已构建的所有映射器的互映关系。

配置步骤用于协调和初始化映射类之间的relationship()链接,以及调用配置事件,如MapperEvents.before_configured()MapperEvents.after_configured(),这些事件可能被 ORM 扩展或用户定义的扩展钩子使用。

映射器配置通常是自动调用的,第一次使用特定 registry 的映射时,以及每当使用映射并且已经构造了额外的尚未配置的映射器时。然而,自动配置过程仅局限于涉及目标映射器和任何相关的 registry 对象的 registry;这相当于在特定 registry 上调用 registry.configure() 方法。

与之相比,configure_mappers() 函数将在内存中存在的所有 registry 对象上调用配置过程,并且可能对使用许多个体 registry 对象但彼此相关的场景有用。

从版本 1.4 开始更改:从 SQLAlchemy 1.4.0b2 开始,此函数按照每个 registry 的方式工作,定位所有存在的 registry 对象并调用每个对象上的 registry.configure() 方法。可能更喜欢使用 registry.configure() 方法来限制映射器的配置仅限于特定 registry 和/或声明性基类。

自动配置被调用的点包括当映射类被实例化为实例时,以及当使用 Session.query()Session.execute() 发出 ORM 查询时使用 ORM 启用的语句。

映射器配置过程,无论是由 configure_mappers() 还是 registry.configure() 调用,都提供了几个可用于增强映射器配置步骤的事件挂钩。这些挂钩包括:

  • MapperEvents.before_configured() - 在 configure_mappers()registry.configure() 执行任何工作之前调用一次;这可用于在操作继续之前建立其他选项、属性或相关映射。

  • MapperEvents.mapper_configured() - 在进程中配置每个单独的 Mapper 时调用;将包括除其他映射器设置的反向引用之外的所有映射器状态,这些映射器尚未配置。

  • MapperEvents.after_configured() - 在 configure_mappers()registry.configure() 完成后调用一次;在此阶段,所有配置操作范围内的 Mapper 对象将被完全配置。请注意,调用应用程序可能仍然有其他尚未生成的映射,例如,如果它们在尚未导入的模块中,还可能有映射尚未配置,如果它们位于当前配置范围之外的其他registry集合中。

function sqlalchemy.orm.clear_mappers() → None

删除所有类的所有映射器。

从版本 1.4 开始变更:这个函数现在定位所有的registry对象,并调用每个对象的 registry.dispose() 方法。

这个函数从类中删除所有的仪器,并处置它们的关联映射器。一旦调用,这些类将被取消映射,以后可以用新的映射器重新映射。

clear_mappers() 是正常使用,因为在非常特定的测试场景之外,它实际上没有任何有效用途。通常,映射器是用户定义类的永久结构组件,绝不会独立于其类被丢弃。如果映射类本身被垃圾回收,其映射器也将被自动处理。因此,clear_mappers() 仅用于在测试套件中重复使用相同类的不同映射的情况下,这本身是一个极为罕见的用例 - 唯一的这种用例实际上是 SQLAlchemy 自己的测试套件,可能是其他 ORM 扩展库的测试套件,这些库打算在一组固定的类上测试各种映射构造的组合。

function sqlalchemy.orm.util.identity_key(class_: Type[_T] | None = None, ident: Any | Tuple[Any, ...] = None, *, instance: _T | None = None, row: Row[Any] | RowMapping | None = None, identity_token: Any | None = None) → _IdentityKeyType[_T]

生成“标识键”元组,用作 Session.identity_map 字典中的键。

此函数有几种调用样式:

  • identity_key(class, ident, identity_token=token)

    此形式接收一个映射类和一个主键标量或元组作为参数。

    例如:

    >>> identity_key(MyClass, (1, 2))
    (<class '__main__.MyClass'>, (1, 2), None)
    

    参数类:

    映射类(必须是一个位置参数)

    参数 ident:

    主键,可以是标量或元组参数。

    参数 identity_token:

    可选的标识令牌

    版本 1.2 中的新功能:添加了 identity_token

  • identity_key(instance=instance)

    此形式将为给定实例生成标识键。实例不必是持久的,只需其主键属性被填充(否则键将包含这些缺失值的 None)。

    例如:

    >>> instance = MyClass(1, 2)
    >>> identity_key(instance=instance)
    (<class '__main__.MyClass'>, (1, 2), None)
    

    在此形式中,给定实例最终将通过 Mapper.identity_key_from_instance() 运行,如果对象已过期,则将执行相应行的数据库检查。

    参数实例:

    对象实例(必须作为关键字参数给出)

  • identity_key(class, row=row, identity_token=token)

    此形式类似于类/元组形式,但是传递了数据库结果行作为 RowRowMapping 对象。

    例如:

    >>> row = engine.execute(\
     text("select * from table where a=1 and b=2")\
     ).first()
    >>> identity_key(MyClass, row=row)
    (<class '__main__.MyClass'>, (1, 2), None)
    

    参数类:

    映射类(必须是一个位置参数)

    参数行:

    RowCursorResult 返回的行(必须作为关键字参数给出)

    参数 identity_token:

    可选的标识令牌

    版本 1.2 中的新功能:添加了 identity_token

function sqlalchemy.orm.polymorphic_union(table_map, typecolname, aliasname='p_union', cast_nulls=True)

创建一个多态映射器使用的 UNION 语句。

请参见具体表继承以了解如何使用此功能。

参数:

  • table_map – 将多态标识映射到 Table 对象。

  • typecolname – “鉴别器”列的字符串名称,该列将从查询中派生,为每一行产生多态标识。如果为 None,则不生成多态鉴别器。

  • aliasname – 生成的 alias() 构造的名称。

  • cast_nulls – 如果为 True,则不存在的列,表示为标记的 NULL 值,将被传递到 CAST 中。这是一种问题的传统行为,对于某些后端(如 Oracle)存在问题 - 在这种情况下,可以将其设置为 False。

function sqlalchemy.orm.orm_insert_sentinel(name: str | None = None, type_: _TypeEngineArgument[Any] | None = None, *, default: Any | None = None, omit_from_statements: bool = True) → MappedColumn[Any]

提供了一个虚拟的 mapped_column(),它生成所谓的 sentinel 列,允许对于不具有合格的主键配置的表进行具有确定性的 RETURNING 排序的高效批量插入。

使用orm_insert_sentinel()类似于在 Core Table 构造中使用insert_sentinel() 构造的用法。

将此构造添加到声明式映射类的指南与insert_sentinel() 构造的相同;数据库表本身也需要具有此名称的列。

关于此对象的使用背景,请参阅 配置 Sentinel 列 作为 “INSERT 语句的“插入多个值”行为 部分的一部分。

另请参见

insert_sentinel()

“INSERT 语句的“插入多个值”行为

配置 Sentinel 列

2.0.10 版本中的新增内容。

function sqlalchemy.orm.reconstructor(fn)

将一个方法装饰为‘reconstructor’挂钩。

将单个方法指定为“reconstructor”,一个类似于__init__方法的方法,ORM 在实例从数据库中加载或者以其他方式重新构建后会调用该方法。

提示

reconstructor() 装饰器使用了 InstanceEvents.load() 事件挂钩,该事件可以直接使用。

重构器将在没有参数的情况下被调用。实例的标量(非集合)数据库映射属性将在函数内可用。急切加载的集合通常尚不可用,并且通常只包含第一个元素。在这个阶段对对象进行的 ORM 状态更改不会被记录到下一个 flush()操作中,因此重构器内的活动应该保守。

另请参阅

InstanceEvents.load()

class sqlalchemy.orm.Mapper

定义了 Python 类与数据库表或其他关系结构之间的关联,以便对该类进行 ORM 操作。

Mapper对象是使用registry对象上存在的映射方法实例化的。有关实例化新Mapper对象的信息,请参阅 ORM 映射类概述。

成员

init(), add_properties(), add_property(), all_orm_descriptors, attrs, base_mapper, c, cascade_iterator(), class_, class_manager, column_attrs, columns, common_parent(), composites, concrete, configured, entity, get_property(), get_property_by_column(), identity_key_from_instance(), identity_key_from_primary_key(), identity_key_from_row(), inherits, is_mapper, is_sibling(), isa(), iterate_properties, local_table, mapped_table, mapper, non_primary, persist_selectable, polymorphic_identity, polymorphic_iterator(), polymorphic_map, polymorphic_on, primary_key, primary_key_from_instance(), primary_mapper(), relationships, selectable, self_and_descendants, single, synonyms, tables, validators, with_polymorphic_mappers

类签名

sqlalchemy.orm.Mapper (sqlalchemy.orm.ORMFromClauseRolesqlalchemy.orm.ORMEntityColumnsClauseRolesqlalchemy.sql.cache_key.MemoizedHasCacheKeysqlalchemy.orm.base.InspectionAttrsqlalchemy.log.Identifiedsqlalchemy.inspection.Inspectablesqlalchemy.event.registry.EventTargettyping.Generic)

method __init__(class_: Type[_O], local_table: FromClause | None = None, properties: Mapping[str, MapperProperty[Any]] | None = None, primary_key: Iterable[_ORMColumnExprArgument[Any]] | None = None, non_primary: bool = False, inherits: Mapper[Any] | Type[Any] | None = None, inherit_condition: _ColumnExpressionArgument[bool] | None = None, inherit_foreign_keys: Sequence[_ORMColumnExprArgument[Any]] | None = None, always_refresh: bool = False, version_id_col: _ORMColumnExprArgument[Any] | None = None, version_id_generator: Literal[False] | Callable[[Any], Any] | None = None, polymorphic_on: _ORMColumnExprArgument[Any] | str | MapperProperty[Any] | None = None, _polymorphic_map: Dict[Any, Mapper[Any]] | None = None, polymorphic_identity: Any | None = None, concrete: bool = False, with_polymorphic: _WithPolymorphicArg | None = None, polymorphic_abstract: bool = False, polymorphic_load: Literal['selectin', 'inline'] | None = None, allow_partial_pks: bool = True, batch: bool = True, column_prefix: str | None = None, include_properties: Sequence[str] | None = None, exclude_properties: Sequence[str] | None = None, passive_updates: bool = True, passive_deletes: bool = False, confirm_deleted_rows: bool = True, eager_defaults: Literal[True, False, 'auto'] = 'auto', legacy_is_orphan: bool = False, _compiled_cache_size: int = 100)

一个新的 Mapper 对象的直接构造函数。

不直接调用 Mapper 构造函数,通常通过使用 registry 对象通过声明式或命令式映射样式调用。

在 2.0 版本中进行了更改:公开的 mapper() 函数已移除;对于传统的映射配置,请使用 registry.map_imperatively() 方法。

下面记录的参数可以传递给 registry.map_imperatively() 方法,或者可以在具有声明性的 Mapper 配置选项中描述的 __mapper_args__ 声明类属性中传递。

参数:

  • class_ – 要映射的类。在使用声明式时,此参数将自动传递为声明的类本身。

  • local_table – 要映射到的 Table 或其他 FromClause(即可选择的)。如果此映射器使用单表继承从另一个映射器继承,则可以为 None。在使用声明式时,此参数由扩展自动传递,根据通过 DeclarativeBase.__table__ 属性配置的内容或通过 DeclarativeBase.__tablename__ 属性的结果产生的 Table

  • polymorphic_abstract

    表示此类将在多态层次结构中映射,但不会直接实例化。该类通常被映射,只是在继承层次结构中没有对 Mapper.polymorphic_identity 的要求。但是,该类必须是使用基类中的 Mapper.polymorphic_on 的多态继承方案的一部分。

    2.0 版中的新功能。

    另请参见

    使用 polymorphic_abstract 构建更深层次的层次结构

  • always_refresh – 如果为 True,则为此映射类的所有查询操作将覆盖已存在于会话中的对象实例中的所有数据,用从数据库加载的任何信息擦除任何内存中的更改。强烈不建议使用此标志;作为替代方案,请参见方法 Query.populate_existing()

  • allow_partial_pks – 默认为 True。表示具有一些 NULL 值的复合主键应被视为可能存在于数据库中。这会影响映射器是否将传入的行分配给现有标识,以及 Session.merge() 是否首先检查数据库中特定主键值。例如,如果已映射到 OUTER JOIN,则可能会出现“部分主键”。

  • batch – 默认为 True,表示可以将多个实体的保存操作一起批处理以提高效率。将其设置为 False 表示在保存下一个实例之前将完全保存一个实例。这在极为罕见的情况下使用,即 MapperEvents 监听器需要在单个行持久性操作之间被调用的情况下。

  • column_prefix

    一个字符串,当将 Column 对象自动分配为映射类的属性时,将会在映射属性名称之前添加。不影响在 Mapper.properties 字典中显式映射的 Column 对象。

    此参数通常与将 Table 对象保持分开的命令式映射一起使用。假设 user_table Table 对象具有名为 user_iduser_namepassword 的列:

    class User(Base):
        __table__ = user_table
        __mapper_args__ = {'column_prefix':'_'}
    

    上述映射将 user_iduser_namepassword 列分配给映射的 User 类上名为 _user_id_user_name_password 的属性。

    Mapper.column_prefix 参数在现代用法中不常见。对于处理反射表,更灵活的自动命名方案是拦截反射时的 Column 对象;请参阅从反射表自动化列命名方案一节中关于此用法模式的注释。

  • concrete

    如果为 True,则表示此映射器应使用具体表继承与其父映射器。

    请参阅具体表继承中的示例。

  • confirm_deleted_rows – 默认为 True;当基于特定主键发生 DELETE 时,如果匹配的行数不等于预期的行数,则会发出警告。可以将此参数设置为 False,以处理数据库 ON DELETE CASCADE 规则可能自动删除某些行的情况。警告可能在将来的版本中更改为异常。

  • eager_defaults

    如果为 True,则 ORM 将在 INSERT 或 UPDATE 后立即获取服务器生成的默认值的值,而不是将其保留为过期以在下次访问时获取。这可以用于需要在 flush 完成之前立即获取服务器生成值的事件方案。

    值的获取可以通过在 INSERTUPDATE 语句中与 RETURNING 一起使用,或者在 INSERTUPDATE 之后添加额外的 SELECT 语句,如果后端不支持 RETURNING

    使用 RETURNING 对于 SQLAlchemy 可以利用 insertmanyvalues 特别适用于 INSERT 语句,而使用额外的 SELECT 相对性能较差,增加了额外的 SQL 往返,如果这些新属性不被访问,则这些往返是不必要的。

    因此,Mapper.eager_defaults 默认为字符串值"auto",表示应该使用 RETURNING 获取 INSERT 的服务器默认值,如果后端数据库支持的话,并且如果正在使用的方言支持“insertmanyreturning”作为 INSERT 语句。如果后端数据库不支持 RETURNING 或者“insertmanyreturning”不可用,则不会获取服务器默认值。

    从版本 2.0.0rc1 开始更改:为 Mapper.eager_defaults 添加了“auto”选项

    另请参阅

    获取服务器生成的默认值

    从版本 2.0.0 开始更改:RETURNING现在可以同时使用插入多行的 insertmanyvalues 功能,这使得支持的后端上的Mapper.eager_defaults特性性能非常高。

  • exclude_properties

    排除映射的字符串列名列表或集合。

    另请参见

    映射表列的子集

  • include_properties

    要映射的字符串列名的包含列表或集合。

    另请参见

    映射表列的子集

  • inherits

    映射类或其中一个的对应Mapper,指示此Mapper应从中继承的超类。此处映射的类必须是另一个映射器类的子类。在使用声明式时,此参数会自动传递,因为已声明类的自然类层次结构。

    另请参见

    映射类继承层次结构

  • inherit_condition – 对于联接表继承,定义两个表如何连接的 SQL 表达式;默认为两个表之间的自然连接。

  • inherit_foreign_keys – 当使用inherit_condition并且存在的列缺少ForeignKey配置时,可以使用此参数来指定哪些列是“外键”。在大多数情况下可以保持为None

  • legacy_is_orphan

    布尔值,默认为False。当为True时,指定对由此映射器映射的对象应用“传统”孤立考虑,这意味着仅当它从指向此映射器的所有父级中解除关联时,即将删除孤立级联的挂起(即,非持久性)对象才会自动从所拥有的Session中清除。新的默认行为是,当对象与指定了delete-orphan级联的任何父级之一解除关联时,对象会自动从其父级中清除。此行为与持久性对象的行为更一致,并允许行为在更多的场景中独立于孤立对象是否已刷新。

    有关此更改的详细信息和示例,请参见将“待处理”对象视为“孤立”对象的考虑更为积极。

  • non_primary

    指定此Mapper

    除了“主”映射器之外,也就是用于持久化的映射器。在此创建的Mapper可用于将类的临时映射到备用可选择的对象上,仅用于加载。

    自版本 1.3 起已弃用:mapper.non_primary参数已弃用,并将在将来的发布版本中删除。非主映射器的功能现在更适合使用AliasedClass构造,1.3 中也可以作为relationship()的目标使用。

    另请参阅

    与别名类的关系 - 新模式,消除了Mapper.non_primary标志的需要。

  • passive_deletes -

    指示在删除联合表继承实体时外键列的 DELETE 行为。基本映射器默认为False;对于继承映射器,默认为False,除非在超类映射器上将值设置为True

    当为True时,假定已在将此映射器的表与其超类表链接的外键关系上配置了 ON DELETE CASCADE,以便当工作单元尝试删除实体时,只需为超类表发出 DELETE 语句,而不是为此表发出 DELETE 语句。

    当为False时,将为此映射器的表分别发出 DELETE 语句。如果此表的本地主键属性未加载,则必须发出 SELECT 以验证这些属性;请注意,联合表子类的主键列不是对象整体的“主键”部分。

    请注意,True的值始终强制应用于子类映射器;也就是说,超类无法指定无主动删除而不对所有子类映射器产生影响。

    另请参阅

    在 ORM 关系中使用外键 ON DELETE 级联 - 描述了与relationship()一起使用的类似功能。

    mapper.passive_updates - 支持联合表继承映射的 ON UPDATE CASCADE

  • passive_updates -

    指示联合表继承映射中主键列更改时外键列的 UPDATE 行为。默认为True

    当为 True 时,假定数据库上的外键已配置为 ON UPDATE CASCADE,并且数据库将处理从源列到联合表行上的依赖列的 UPDATE 传播。

    当为 False 时,假定数据库不执行参照完整性,并且不会为更新发出自己的 CASCADE 操作。在主键更改期间,工作单元过程将针对依赖列发出 UPDATE 语句。

    另请参阅

    可变主键 / 更新级联 - 描述与 relationship() 一起使用的类似功能的说明

    mapper.passive_deletes - 为连接表继承映射器支持 ON DELETE CASCADE

  • polymorphic_load

    在继承层次结构中的子类中指定“多态加载”行为(仅适用于连接和单表继承)。有效值为:

    • “‘inline’” - 指定此类应该是“with_polymorphic”映射器的一部分,例如,它的列将包含在针对基础的 SELECT 查询中。
    • “‘selectin’” - 指定当加载此类的实例时,将发出额外的 SELECT 来检索特定于此子类的列。SELECT 使用 IN 一次性检索多个子类。

    版本 1.2 中的新功能。

    另请参阅

    在映射器上配置 with_polymorphic()

    使用 selectin_polymorphic()

  • polymorphic_on

    指定用于确定传入行的目标类的列、属性或 SQL 表达式,当存在继承类时。

    可以指定为字符串属性名称,也可以指定为 SQL 表达式,例如 Column 或在声明性映射中为 mapped_column() 对象。通常期望 SQL 表达式对应于基础映射的最底层映射的 Table 中的列:

    class Employee(Base):
        __tablename__ = 'employee'
    
        id: Mapped[int] = mapped_column(primary_key=True)
        discriminator: Mapped[str] = mapped_column(String(50))
    
        __mapper_args__ = {
            "polymorphic_on":discriminator,
            "polymorphic_identity":"employee"
        }
    

    它也可以指定为 SQL 表达式,如此示例中我们使用 case() 构造来提供条件方法:

    class Employee(Base):
        __tablename__ = 'employee'
    
        id: Mapped[int] = mapped_column(primary_key=True)
        discriminator: Mapped[str] = mapped_column(String(50))
    
        __mapper_args__ = {
            "polymorphic_on":case(
                (discriminator == "EN", "engineer"),
                (discriminator == "MA", "manager"),
                else_="employee"),
            "polymorphic_identity":"employee"
        }
    

    它也可能使用其字符串名称引用任何属性,在使用注释列配置时特别有用:

    class Employee(Base):
        __tablename__ = 'employee'
    
        id: Mapped[int] = mapped_column(primary_key=True)
        discriminator: Mapped[str]
    
        __mapper_args__ = {
            "polymorphic_on": "discriminator",
            "polymorphic_identity": "employee"
        }
    

    当将 polymorphic_on 设置为引用不存在于本地映射的 Table 中的属性或表达式时,但是鉴别器的值应该持久化到数据库中时,鉴别器的值不会自动设置在新实例上;这必须由用户处理,可以通过手动方式或通过事件监听器来处理。建立这样一个监听器的典型方法如下所示:

    from sqlalchemy import event
    from sqlalchemy.orm import object_mapper
    
    @event.listens_for(Employee, "init", propagate=True)
    def set_identity(instance, *arg, **kw):
        mapper = object_mapper(instance)
        instance.discriminator = mapper.polymorphic_identity
    

    在上述情况下,我们将映射类的polymorphic_identity值分配给discriminator属性,从而将该值持久化到数据库中的discriminator列中。

    警告

    目前,只能设置一个鉴别器列,通常在层次结构中的最底层类上。尚不支持“级联”多态列。

    参见

    映射类继承层次结构

  • polymorphic_identity

    指定由Mapper.polymorphic_on设置引用的列表达式返回的值,用于识别此特定类的值。当接收到行时,与Mapper.polymorphic_on列表达式对应的值将与此值进行比较,指示应使用哪个子类来重建新对象。

    参见

    映射类继承层次结构

  • properties

    将对象属性的字符串名称映射到MapperProperty实例的字典,这些实例定义了该属性的持久化行为。请注意,在映射到映射TableColumn对象时,除非被覆盖,否则会自动将其放置到ColumnProperty实例中。使用声明时,此参数将根据在声明类体中声明的所有这些MapperProperty实例自动传递。

    参见

    属性字典 - 在 ORM 映射类概述中

  • primary_key

    一组Column对象,或者是指向Column的属性名称的字符串名称,这些属性定义了要针对此映射器的可选择单元使用的主键。这通常只是local_table的主键,但可以在此处进行覆盖。

    从版本 2.0.2 开始更改:Mapper.primary_key参数也可以表示为字符串属性名称。

    参见

    映射到一组显式主键列 - 背景和示例用法

  • version_id_col

    用于保持表中行的运行版本 ID 的Column。这用于检测并发更新或刷新中存在过时数据的存在。方法是检测如果 UPDATE 语句与最后已知的版本 ID 不匹配,则抛出StaleDataError异常。默认情况下,列必须是Integer类型,除非version_id_generator指定了替代版本生成器。

    另请参阅

    配置版本计数器 - 版本计数和原理的讨论。

  • version_id_generator

    定义如何生成新版本 ID。默认为None,表示采用简单的整数计数方案。要提供自定义版本计数方案,请提供一个形如以下的可调用函数:

    def generate_version(version):
        return next_version
    

    或者,可以使用服务器端版本控制功能,例如触发器,或者在版本 ID 生成器之外的程序化版本控制方案,通过指定值False。请参阅服务器端版本计数器以了解在使用此选项时的重要要点的讨论。

    另请参阅

    自定义版本计数器/类型

    服务器端版本计数器

  • with_polymorphic

    一个形如(<classes>, <selectable>)的元组,表示“多态”加载的默认样式,即一次查询哪些表。<classes>是任何指示一次加载的继承类的单个或列表的映射器和/或类。特殊值'*'可用于指示应立即加载所有后代类。第二个元组参数<selectable>指示将用于查询多个类的可选择项。

    在现代映射中,Mapper.polymorphic_load参数可能比使用Mapper.with_polymorphic更可取,以指示多态加载样式的子类技术。

    另请参阅

    在映射器上配置 with_polymorphic()

method add_properties(dict_of_properties)

将给定的属性字典添加到此映射器中,使用add_property

method add_property(key: str, prop: Column[Any] | MapperProperty[Any]) → None

向此映射器添加单个 MapperProperty。

如果尚未配置映射器,则只需将属性添加到发送到构造函数的初始属性字典中。如果此映射器已配置,则立即配置给定的 MapperProperty。

attribute all_orm_descriptors

一个包含与映射类关联的所有InspectionAttr属性的命名空间。

这些属性在所有情况下都是与映射类或其超类关联的 Python 描述符。

此命名空间包括映射到类的属性以及由扩展模块声明的属性。它包括任何从InspectionAttr继承的 Python 描述符类型。这包括QueryableAttribute,以及扩展类型,如hybrid_propertyhybrid_methodAssociationProxy

为了区分映射属性和扩展属性,属性InspectionAttr.extension_type将引用一个常量,用于区分不同的扩展类型。

属性的排序基于以下规则:

  1. 从子类到超类按顺序迭代类及其超类(即通过cls.__mro__迭代)

  2. 对于每个类,按照它们在__dict__中出现的顺序生成属性,但以下步骤除外。在 Python 3.6 及以上版本中,此顺序将与类的构造相同,但有一个例外,即应用程序或映射器后来添加的属性。

  3. 如果某个属性键也在超类__dict__中,那么它将包含在该类的迭代中,而不是它首次出现的类中。

上述过程产生了一种确定性排序,该排序是根据属性被分配给类的顺序确定的。

自版本 1.3.19 更改:确保对Mapper.all_orm_descriptors()的确定性排序。

当处理QueryableAttribute时,QueryableAttribute.property属性引用了MapperProperty属性,当通过Mapper.attrs引用映射属性集合时,将得到它。

警告

Mapper.all_orm_descriptors访问器命名空间是OrderedProperties的一个实例。这是一个类似字典的对象,包括一小部分命名方法,如OrderedProperties.items()OrderedProperties.values()。当动态访问属性时,建议使用字典访问方案,例如mapper.all_orm_descriptors[somename],而不是getattr(mapper.all_orm_descriptors, somename),以避免名称冲突。

另请参阅

Mapper.attrs

attribute attrs

该映射器的所有MapperProperty对象的命名空间。

这是一个根据其键名提供每个属性的对象。例如,具有User.name属性的User类的映射器将提供mapper.attrs.name,这将是代表name列的ColumnProperty。命名空间对象还可以进行迭代,这将产生每个MapperProperty

Mapper具有该属性的几个预过滤视图,限制了返回的属性类型,包括synonymscolumn_attrsrelationshipscomposites

警告

Mapper.attrs访问器命名空间是OrderedProperties的一个实例。这是一个类似字典的对象,包括一小部分命名方法,如OrderedProperties.items()OrderedProperties.values()。当动态访问属性时,建议使用字典访问方案,例如mapper.attrs[somename],而不是getattr(mapper.attrs, somename),以避免名称冲突。

另请参阅

Mapper.all_orm_descriptors

attribute base_mapper: Mapper[Any]

继承链中最基础的Mapper

在非继承场景中,此属性始终为此Mapper。在继承场景中,它引用继承链中所有其他Mapper对象的父级Mapper

这是在映射器构建期间确定的只读属性。如果直接修改,行为未定义。

attribute c: ReadOnlyColumnCollection[str, Column[Any]]

Mapper.columns的同义词。

method cascade_iterator(type_: str, state: InstanceState[_O], halt_on: Callable[[InstanceState[Any]], bool] | None = None) → Iterator[Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict]]

遍历对象图中的每个元素及其映射器,对于符合给定级联规则的所有关系。

参数:

  • type_

    级联规则的名称(即"save-update""delete"等)。

    注意

    在此处不接受"all"级联。有关通用对象遍历函数,请参阅如何遍历与给定对象相关的所有对象?。

  • state – 主要的 InstanceState。子项将根据为此对象的映射器定义的关系进行处理。

返回:

该方法产生单个对象实例。

另请参阅

级联

如何遍历与给定对象相关的所有对象? - 演示了一个通用函数,用于遍历所有对象而不依赖于级联。

attribute class_: Type[_O]

Mapper映射到的类。

attribute class_manager: ClassManager[_O]

ClassManager维护此Mapper的事件监听器和类绑定描述符。

这是在映射器构建期间确定的只读属性。如果直接修改,行为是未定义的。

attribute column_attrs

返回此Mapper维护的所有ColumnProperty属性的命名空间。

另请参阅

Mapper.attrs - 所有MapperProperty对象的命名空间。

attribute columns: ReadOnlyColumnCollection[str, Column[Any]]

由此Mapper维护的Column或其他标量表达式对象的集合。

该集合的行为与任何Table对象上的c属性相同,只是此映射中包含的列,且基于映射中定义的属性名称进行键控,而不一定是Column本身的key属性。此外,由column_property()映射的标量表达式也在此处。

这是在映射器构建期间确定的只读属性。如果直接修改,行为是未定义的。

method common_parent(other: Mapper[Any]) → bool

如果给定的映射器与此映射器共享一个共同的继承父级,则返回 true。

attribute composites

返回此Mapper维护的所有Composite属性的命名空间。

另请参阅

Mapper.attrs - 所有MapperProperty对象的命名空间。

attribute concrete: bool

如果此Mapper是具体继承映射器,则表示True

这是在映射器构建期间确定的只读属性。如果直接修改,行为是未定义的。

attribute configured: bool = False

如果已配置此Mapper,则表示True

这是在映射器构建期间确定的只读属性。如果直接修改,行为是未定义的。

另请参阅

configure_mappers()

attribute entity

检查 API 的一部分。

返回 self.class_。

method get_property(key: str, _configure_mappers: bool = False) → MapperProperty[Any]

返回与给定键关联的 MapperProperty。

method get_property_by_column(column: ColumnElement[_T]) → MapperProperty[_T]

给定Column对象,返回映射到此列的MapperProperty

method identity_key_from_instance(instance: _O) → _IdentityKeyType[_O]

根据其主键属性返回给定实例的标识键。

如果实例的状态已过期,则调用此方法将导致数据库检查以查看对象是否已被删除。如果行不再存在,则引发ObjectDeletedError

此值通常也在实例状态下以属性名称键的形式找到。

method identity_key_from_primary_key(primary_key: Tuple[Any, ...], identity_token: Any | None = None) → _IdentityKeyType[_O]

返回一个用于在标识映射中存储/检索项目的标识映射键。

参数:

primary_key - 表示标识符的值列表。

method identity_key_from_row(row: Row[Any] | RowMapping | None, identity_token: Any | None = None, adapter: ORMAdapter | None = None) → _IdentityKeyType[_O]

返回用于在标识映射中存储/检索项目的标识映射键。

参数:

-

从选择了 ORM 映射的主键列的结果集生成的RowRowMapping

从版本 2.0 开始:RowRowMapping被接受作为“row”参数

attribute inherits: Mapper[Any] | None

引用此Mapper继承自的Mapper(如果有)。

attribute is_mapper = True

检查 API 的一部分。

method is_sibling(other: Mapper[Any]) → bool

如果另一个映射器是此映射器的继承兄弟,则返回 true。共同的父级但不同的分支

method isa(other: Mapper[Any]) → bool

如果此映射器从给定的映射器继承,则返回 True。

attribute iterate_properties

返回所有 MapperProperty 对象的迭代器。

attribute local_table: FromClause

Mapper所引用的直接FromClause

通常是Table的一个实例,可以是任何FromClause

“本地”表是Mapper直接负责管理的可选择的表,从属性访问和 flush 的角度来看。对于非继承映射器,Mapper.local_table将与Mapper.persist_selectable相同。对于继承映射器,Mapper.local_table指的是包含该Mapper正在加载/持久化的列的特定部分,例如加入中的特定Table

另请参阅

Mapper.persist_selectable

Mapper.selectable.

attribute mapped_table

自版本 1.3 起已弃用:使用 .persist_selectable

attribute mapper

是检查 API 的一部分。

返回自身。

attribute non_primary: bool

如果此Mapper是“非主”映射器,例如仅用于选择行而不用于持久化管理,则表示为 True

这是在映射器构造期间确定的只读属性。如果直接修改,则行为未定义。

attribute persist_selectable: FromClause

Mapper映射到的FromClause

通常是Table的一个实例,可以是任何FromClause

Mapper.persist_selectable类似于Mapper.local_table,但表示继承方案中整体表示继承类层次结构的FromClause

:attr..Mapper.persist_selectable也与Mapper.selectable属性分开,后者可能是用于选择列的替代子查询。:attr..Mapper.persist_selectable针对的是在持久化操作中将被写入的列。

另请参阅

Mapper.selectable

Mapper.local_table

attribute polymorphic_identity: Any | None

表示一个标识符,该标识符在结果行加载期间与Mapper.polymorphic_on列匹配。

仅在继承时使用,此对象可以是与由Mapper.polymorphic_on表示的列的类型可比较的任何类型。

这是在映射器构造期间确定的只读属性。如果直接修改,则行为未定义。

method polymorphic_iterator() → Iterator[Mapper[Any]]

遍历包括此映射器和所有后代映射器在内的集合。

这不仅包括直接继承的映射器,还包括所有它们的继承映射器。

要遍历整个层次结构,请使用mapper.base_mapper.polymorphic_iterator()

attribute polymorphic_map: Dict[Any, Mapper[Any]]

在继承场景中,将“多态身份”标识符映射到Mapper实例。

标识符可以是与Mapper.polymorphic_on所表示的列的类型可比较的任何类型。

映射器的继承链都将引用相同的多态映射对象。该对象用于将传入的结果行与目标映射器相关联。

这是在映射器构造期间确定的只读属性。如果直接修改,则行为未定义。

attribute polymorphic_on: KeyedColumnElement[Any] | None

Mapperpolymorphic_on参数指定的Column或 SQL 表达式,在继承场景中。

此属性通常是一个Column实例,但也可能是一个表达式,例如从cast()派生的表达式。

这是在映射器构造期间确定的只读属性。如果直接修改,则行为未定义。

attribute primary_key: Tuple[Column[Any], ...]

包含作为此Mapper在表映射的‘主键’的一部分的Column对象的集合的可迭代对象,从此Mapper的角度来看。

这个列表与Mapper.persist_selectable中的可选择项相对。在继承映射器的情况下,一些列可能由超类映射器管理。例如,在Join的情况下,主键由Join引用的所有表的主键列确定。

此列表也不一定与与基础表关联的主键列集合相同;Mapper具有可以覆盖Mapper认为是主键列的primary_key参数。

这是一个只读属性,在映射器构造期间确定。如果直接修改,行为是未定义的。

method primary_key_from_instance(instance: _O) → Tuple[Any, ...]

返回给定实例的主键值列表。

如果实例的状态已过期,则调用此方法将导致数据库检查以查看对象是否已被删除。如果行不再存在,则会引发ObjectDeletedError

method primary_mapper() → Mapper[Any]

返回与此映射器的类键(类)对应的主映射器。

attribute relationships

由此Mapper维护的所有Relationship属性的命名空间。

警告

Mapper.relationships 访问器命名空间是OrderedProperties的实例。这是一个类似于字典的对象,其中包含少量命名方法,例如OrderedProperties.items()OrderedProperties.values()。在动态访问属性时,应优先使用字典访问方案,例如mapper.relationships[somename]而不是getattr(mapper.relationships, somename),以避免名称冲突。

另请参阅

Mapper.attrs - 所有MapperProperty对象的命名空间。

attribute selectable

默认情况下,此Mapper从中选择的FromClause构造。

通常情况下,这等同于persist_selectable,除非使用了with_polymorphic功能,在这种情况下,将返回完整的“多态”可选择项。

attribute self_and_descendants

包括此映射器和所有后代映射器的集合。

这不仅包括直接继承的映射器,还包括所有它们继承的映射器。

attribute single: bool

如果此 Mapper 是单表继承映射器,则表示 True

如果设置了此标志,Mapper.local_table 将为 None

这是在映射器构建期间确定的只读属性。如果直接修改,行为未定义。

attribute synonyms

返回此 Mapper 维护的所有 Synonym 属性的命名空间。

另请参阅

Mapper.attrs - 所有 MapperProperty 对象的命名空间。

attribute tables: Sequence[TableClause]

包含此 Mapper 意识到的所有 TableTableClause 对象的序列。

如果映射器被映射到一个 Join 或者代表 SelectAlias,构成完整结构的各个 Table 对象将在这里表示。

这是在映射器构建期间确定的只读属性。如果直接修改,行为未定义。

attribute validators: util.immutabledict[str, Tuple[str, Dict[str, Any]]]

一个不可变字典,其中属性已使用 validates() 装饰器装饰。

字典包含字符串属性名称作为键,映射到实际验证方法。

attribute with_polymorphic_mappers

默认“多态”查询中包含的 Mapper 对象列表。

class sqlalchemy.orm.MappedAsDataclass

混合类用于指示映射此类时,还将其转换为数据类。

另请参阅

声明性数据类映射 - 完整的 SQLAlchemy 本地数据类映射背景

版本 2.0 中的新功能。

class sqlalchemy.orm.MappedClassProtocol

表示 SQLAlchemy 映射类的协议。

协议对类的类型是通用的,使用 MappedClassProtocol[Any] 来允许任何映射类。

类签名

sqlalchemy.orm.MappedClassProtocol (typing_extensions.Protocol)

posted @ 2024-06-22 11:41  绝不原创的飞龙  阅读(29)  评论(0编辑  收藏  举报