集合自定义和 API 详情


relationship() 函数定义两个类之间的链接。当链接定义一对多或多对多关系时,在加载和作对象时,它将表示为 Python 集合。本节提供有关集合配置和技术的其他信息。


自定义集合访问


映射一对多或多对多关系会生成一个值集合,可通过父实例上的属性访问。这些的两种常见集合类型是 listset,它们在 使用 Mapped 是通过使用 Mapped 容器中的集合类型建立的,如下面的 Parent.children 集合中所示,其中使用了 list

from sqlalchemy import ForeignKey

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


class Base(DeclarativeBase):
    pass


class Parent(Base):
    __tablename__ = "parent"

    parent_id: Mapped[int] = mapped_column(primary_key=True)

    # use a list
    children: Mapped[List["Child"]] = relationship()


class Child(Base):
    __tablename__ = "child"

    child_id: Mapped[int] = mapped_column(primary_key=True)
    parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))


或者对于一组,以相同的方式进行说明 Parent.children 集合:

from typing import Set
from sqlalchemy import ForeignKey

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


class Base(DeclarativeBase):
    pass


class Parent(Base):
    __tablename__ = "parent"

    parent_id: Mapped[int] = mapped_column(primary_key=True)

    # use a set
    children: Mapped[Set["Child"]] = relationship()


class Child(Base):
    __tablename__ = "child"

    child_id: Mapped[int] = mapped_column(primary_key=True)
    parent_id: Mapped[int] = mapped_column(ForeignKey("parent.id"))


注意


如果使用 Python 3.7 或 3.8,则集合的注释需要使用 typing。列出键入。set,例如 Mapped[List[“Child”]]Mapped[set[“child”]];listset Python 内置函数在这些 Python 版本中尚不支持通用注释,例如:

from typing import List


class Parent(Base):
    __tablename__ = "parent"

    parent_id: Mapped[int] = mapped_column(primary_key=True)

    # use a List, Python 3.8 and earlier
    children: Mapped[List["Child"]] = relationship()


当使用没有 Mapped 注解的映射时,例如当使用命令式映射或无类型化 Python 代码,以及在一些特殊情况下,一个 relationship() 始终可以使用 relationship.collection_class参数:

# non-annotated mapping


class Parent(Base):
    __tablename__ = "parent"

    parent_id = mapped_column(Integer, primary_key=True)

    children = relationship("Child", collection_class=set)


class Child(Base):
    __tablename__ = "child"

    child_id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(ForeignKey("parent.id"))


在没有 relationship.collection_classMapped,则默认集合类型为 list


除了 listset 内置函数之外,还支持两种字典,如下面 词典集合 中所述。还支持任何任意可变序列类型都可以设置为目标集合,需要一些额外的配置步骤;这在 自定义集合实现 一节中进行了介绍。


字典集合


将字典用作集合时,需要一些额外的细节。 这是因为对象总是作为列表从数据库加载,并且 key generation 策略才能正确填充字典。 这 attribute_keyed_dict() 函数是迄今为止实现简单字典集合的最常用方法。它生成一个 dictionary 类,该类将 mapped 类的特定属性作为键应用。下面我们映射一个 Item 类,其中包含一个键控到 Note.keyword 属性的 Note 项字典。当使用 attribute_keyed_dict() 时,映射的 注释可以使用 KeyFuncDict 键入 或者只是普通的 dict,如以下示例所示。但是,在这种情况下,relationship.collection_class 参数是必需的,以便 attribute_keyed_dict() 可以适当地参数化:

from typing import Dict
from typing import Optional

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


class Base(DeclarativeBase):
    pass


class Item(Base):
    __tablename__ = "item"

    id: Mapped[int] = mapped_column(primary_key=True)

    notes: Mapped[Dict[str, "Note"]] = relationship(
        collection_class=attribute_keyed_dict("keyword"),
        cascade="all, delete-orphan",
    )


class Note(Base):
    __tablename__ = "note"

    id: Mapped[int] = mapped_column(primary_key=True)
    item_id: Mapped[int] = mapped_column(ForeignKey("item.id"))
    keyword: Mapped[str]
    text: Mapped[Optional[str]]

    def __init__(self, keyword: str, text: str):
        self.keyword = keyword
        self.text = text


Item.notes 是一个字典:

>>> item = Item()
>>> item.notes["a"] = Note("a", "atext")
>>> item.notes.items()
{'a': <__main__.Note object at 0x2eaaf0>}


attribute_keyed_dict() 将确保每个 Note.keyword 属性符合字典中的键。例如,当分配给 Item.notes 时,我们提供的字典键必须与实际 Note 对象的字典键匹配:

item = Item()
item.notes = {
    "a": Note("a", "atext"),
    "b": Note("b", "btext"),
}


attribute_keyed_dict() 用作键的属性根本不需要映射!使用常规 Python @property几乎可以将有关对象的任何细节或细节组合用作键,如下所示,当我们将其建立为 Note.keyword 的元组和 Note.text 字段的前十个字母时:

class Item(Base):
    __tablename__ = "item"

    id: Mapped[int] = mapped_column(primary_key=True)

    notes: Mapped[Dict[str, "Note"]] = relationship(
        collection_class=attribute_keyed_dict("note_key"),
        back_populates="item",
        cascade="all, delete-orphan",
    )


class Note(Base):
    __tablename__ = "note"

    id: Mapped[int] = mapped_column(primary_key=True)
    item_id: Mapped[int] = mapped_column(ForeignKey("item.id"))
    keyword: Mapped[str]
    text: Mapped[str]

    item: Mapped["Item"] = relationship()

    @property
    def note_key(self):
        return (self.keyword, self.text[0:10])

    def __init__(self, keyword: str, text: str):
        self.keyword = keyword
        self.text = text


上面我们添加了一个 Note.item 关系,具有双向 relationship.back_populates配置。将 Note (注释) 分配给此反向关系 添加到 Item.notes 字典中,并且会自动为我们生成密钥:

>>> item = Item()
>>> n1 = Note("a", "atext")
>>> n1.item = item
>>> item.notes
{('a', 'atext'): <__main__.Note object at 0x2eaaf0>}


其他内置字典类型包括 column_keyed_dict(),它几乎类似于 attribute_keyed_dict(),只是给定了 Column object 直接访问:

from sqlalchemy.orm import column_keyed_dict


class Item(Base):
    __tablename__ = "item"

    id: Mapped[int] = mapped_column(primary_key=True)

    notes: Mapped[Dict[str, "Note"]] = relationship(
        collection_class=column_keyed_dict(Note.__table__.c.keyword),
        cascade="all, delete-orphan",
    )


以及 mapped_collection() ,它被传递给任何可调用的函数。请注意,如前所述,将 attribute_keyed_dict()@property 一起使用通常更容易:

from sqlalchemy.orm import mapped_collection


class Item(Base):
    __tablename__ = "item"

    id: Mapped[int] = mapped_column(primary_key=True)

    notes: Mapped[Dict[str, "Note"]] = relationship(
        collection_class=mapped_collection(lambda note: note.text[0:10]),
        cascade="all, delete-orphan",
    )


字典映射通常与 “Association Proxy” 扩展结合使用,以生成简化的字典视图。请参阅代理到基于字典的集合复合关联代理 例如。


处理 Key Mutation 和 Dictionary 集合的反向填充


使用 attribute_keyed_dict() 时,字典的 “key” 取自目标对象上的属性。对此密钥的更改 不会被跟踪。这意味着必须在首次使用时分配键,如果键发生更改,则不会更改集合。这可能是一个典型的示例是,当依赖 backrefs 来填充属性映射集合时。给定以下内容:

class A(Base):
    __tablename__ = "a"

    id: Mapped[int] = mapped_column(primary_key=True)

    bs: Mapped[Dict[str, "B"]] = relationship(
        collection_class=attribute_keyed_dict("data"),
        back_populates="a",
    )


class B(Base):
    __tablename__ = "b"

    id: Mapped[int] = mapped_column(primary_key=True)
    a_id: Mapped[int] = mapped_column(ForeignKey("a.id"))
    data: Mapped[str]

    a: Mapped["A"] = relationship(back_populates="bs")


在上面,如果我们创建一个引用特定 A()B(),则反向填充会将 B() 添加到 A.bs 集合中,但是如果 B.data 的值尚未设置,则键将为 None

>>> a1 = A()
>>> b1 = B(a=a1)
>>> a1.bs
{None: <test3.B object at 0x7f7b1023ef70>}


在事后设置 b1.data 不会更新集合:

>>> b1.data = "the key"
>>> a1.bs
{None: <test3.B object at 0x7f7b1023ef70>}


如果尝试在构造函数中设置 B() ,也可以看到这一点。参数的顺序会改变结果:

>>> B(a=a1, data="the key")
<test3.B object at 0x7f7b10114280>
>>> a1.bs
{None: <test3.B object at 0x7f7b10114280>}


与:

>>> B(data="the key", a=a1)
<test3.B object at 0x7f7b10114340>
>>> a1.bs
{'the key': <test3.B object at 0x7f7b10114340>}


如果以这种方式使用 backrefs,请确保使用 __init__ 方法以正确的顺序填充属性。


事件处理程序(如下所示)也可用于跟踪集合中的更改:

from sqlalchemy import event
from sqlalchemy.orm import attributes


@event.listens_for(B.data, "set")
def set_item(obj, value, previous, initiator):
    if obj.a is not None:
        previous = None if previous == attributes.NO_VALUE else previous
        obj.a.bs[value] = obj
        obj.a.bs.pop(previous)


自定义集合实现


您也可以将自己的类型用于集合。在简单的情况下,从 listset 继承,添加自定义行为,就足够了。在其他情况下,需要特殊的装饰器来告诉 SQLAlchemy 有关集合如何运行的更多详细信息。


SQLAlchemy 中的集合是透明插桩的。检测意味着跟踪对集合的正常作,并导致在刷新时将更改写入数据库。此外,收集作可能会触发事件,这些事件指示必须执行某些辅助作。辅助作的示例包括将子项保存在父项的 Session 中(即 save-update cascade) 以及同步双向关系的状态 (即 backref())。


collections 包理解 lists、sets 和 dict 的基本接口,并会自动将 instrumentation 应用于这些内置类型及其子类。通过鸭子类型来检测和检测实现基本集合接口的对象派生类型:

class ListLike:
    def __init__(self):
        self.data = []

    def append(self, item):
        self.data.append(item)

    def remove(self, item):
        self.data.remove(item)

    def extend(self, items):
        self.data.extend(items)

    def __iter__(self):
        return iter(self.data)

    def foo(self):
        return "foo"


appendremoveextendlist 的已知成员,将自动进行检测。__iter__ 不是 mutator 方法,也不会被 instrumented,foo 也不会。


当然,鸭子打字(即猜测)并不是坚如磐石的,所以你可以 明确说明您正在实现的接口,方法是提供 __emulates__ class 属性:

class SetLike:
    __emulates__ = set

    def __init__(self):
        self.data = set()

    def append(self, item):
        self.data.add(item)

    def remove(self, item):
        self.data.remove(item)

    def __iter__(self):
        return iter(self.data)


这个类看起来类似于 Python 列表(即“类似列表”),因为它有一个 append 方法,但 __emulates__ 属性会强制将其视为一个集合remove 已知是 set 接口的一部分,将被检测。


但是这个类还不能完全工作:需要一点胶水来适应 SQLAlchemy 的使用。ORM 需要知道使用哪些方法来附加、删除和迭代集合的成员。当使用类似 listset 时,适当的方法都是已知的,并在存在时自动使用。但是,上面的类仅大致类似于一个集合,没有提供预期的 add 方法,因此我们必须向 ORM 指示将代替 add 方法的方法,在本例中使用装饰器 @collection.appender;这将在下一节中进行说明。


通过 Decorator 注释自定义集合


装饰器可用于标记 ORM 管理集合所需的各个方法。当您的类不完全满足其容器类型的常规接口时,或者当您希望使用其他方法完成工作时,请使用它们。

from sqlalchemy.orm.collections import collection


class SetLike:
    __emulates__ = set

    def __init__(self):
        self.data = set()

    @collection.appender
    def append(self, item):
        self.data.add(item)

    def remove(self, item):
        self.data.remove(item)

    def __iter__(self):
        return iter(self.data)


这就是完成该示例所需的全部内容。SQLAlchemy 将通过 append 方法添加实例。remove__iter__ 是 Set 的默认方法,将用于 remove 和 iteration。默认方法也可以更改:

from sqlalchemy.orm.collections import collection


class MyList(list):
    @collection.remover
    def zark(self, item):
        # do something special...
        ...

    @collection.iterator
    def hey_use_this_instead_for_iteration(self): ...


根本没有要求是 “list-like” 或 “set-like”。集合类可以是任何形状,只要它们具有标记为供 SQLAlchemy 使用的 append、remove 和 iterate 接口。调用 Append 和 Remove 方法时,将映射实体作为单个参数,调用迭代器方法时不带参数,并且必须返回迭代器。


基于字典的自定义集合


KeyFuncDict 类可以用作自定义类型的基类,也可以用作快速添加 dict 的混合 collection 支持。它使用键控函数来委托 __setitem____delitem__

from sqlalchemy.orm.collections import KeyFuncDict


class MyNodeMap(KeyFuncDict):
    """Holds 'Node' objects, keyed by the 'name' attribute."""

    def __init__(self, *args, **kw):
        super().__init__(keyfunc=lambda node: node.name)
        dict.__init__(self, *args, **kw)


当子类化 KeyFuncDict 时,如果 __setitem__()__delitem__() 的用户定义版本在 KeyFuncDict 上调用相同的方法,则应 collection.internally_instrumented() , 修饰。这是因为 KeyFuncDict 上的方法已经插桩了 - 从已经插桩的调用中调用它们可能会导致事件被重复或不恰当地触发,在极少数情况下导致内部状态损坏:

from sqlalchemy.orm.collections import KeyFuncDict, collection


class MyKeyFuncDict(KeyFuncDict):
    """Use @internally_instrumented when your methods
    call down to already-instrumented methods.

    """

    @collection.internally_instrumented
    def __setitem__(self, key, value, _sa_initiator=None):
        # do something with key, value
        super(MyKeyFuncDict, self).__setitem__(key, value, _sa_initiator)

    @collection.internally_instrumented
    def __delitem__(self, key, _sa_initiator=None):
        # do something with key
        super(MyKeyFuncDict, self).__delitem__(key, _sa_initiator)


ORM 像 lists 和 sets 一样理解 dict 接口,并且会 如果您选择子类化,则自动检测所有 “Dict-like” 方法 dict 或在 duck 类型的类中提供类似 dict 的集合行为。您必须装饰 appender 和 remover 方法,但是 - 默认情况下,基本字典接口中没有可供 SQLAlchemy 使用的兼容方法。迭代将通过 values() 进行,除非另有装饰。


插桩和自定义类型


许多自定义类型和现有库类可以按原样用作实体集合类型,而无需多说。但是,请务必注意,插桩过程将修改类型,并自动在方法周围添加装饰器。


这些装饰是轻量级的,在关系之外没有作,但当在其他地方触发时,它们确实会增加不必要的开销。当使用库类作为集合时,最好使用 “trivial subclass” 技巧将装饰限制为仅在关系中使用。例如:

class MyAwesomeList(some.great.library.AwesomeList):
    pass


# ... relationship(..., collection_class=MyAwesomeList)


ORM 将这种方法用于内置函数,当直接使用 listsetdict 时,它会悄悄地替换一个普通的子类。


集合 API


对象名称

描述


attribute_keyed_dict(attr_name, *, [ignore_unpopulated_attribute])


基于字典的集合类型,具有基于属性的键控。

attribute_mapped_collection


基于字典的集合类型,具有基于属性的键控。


column_keyed_dict(mapping_spec, *, [ignore_unpopulated_attribute])


一种基于字典的集合类型,具有基于列的键控。

column_mapped_collection


一种基于字典的集合类型,具有基于列的键控。


keyfunc_mapping(keyfunc, *, [ignore_unpopulated_attribute])


具有任意键控的基于字典的集合类型。

KeyFuncDict


ORM 映射字典类的 Base。

mapped_collection


具有任意键控的基于字典的集合类型。

MappedCollection


ORM 映射字典类的 Base。


函数 sqlalchemy.orm 中。attribute_keyed_dictattr_name: str*ignore_unpopulated_attribute: bool = false 类型[KeyFuncDict[任意任意]


基于字典的集合类型,具有基于属性的键控。


在 2.0 版更改: 已将 attribute_mapped_collection 重命名为 attribute_keyed_dict() 中。


返回一个 KeyFuncDict 工厂,该工厂将根据要添加到字典的 ORM 映射实例上的特定命名属性的值生成新的字典键。


注意


在将对象添加到 Dictionary 集合时,必须为 target 属性的值分配其值。此外,不会跟踪对 key 属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关更多详细信息,请参阅 处理密钥更改和反向填充 Dictionary 集合


另请参阅


词典集合 - 使用背景


参数

  • attr_name – 映射类上 ORM 映射属性的字符串名称,该属性在特定实例上的值将用作该实例的新字典条目的键。

  • ignore_unpopulated_attribute


    如果为 True,并且对象的 target 属性根本未填充,则将以静默方式跳过该作。默认情况下,会引发错误。


    2.0 版本的新Function:如果属性 用于 Dictionary 键时,确定它从未 填充任何值。 这 attribute_keyed_dict.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。 这与 1.x 系列的行为形成鲜明对比,后者 错误地使用任意键填充字典中的值 值为 None


函数 sqlalchemy.orm 中。column_keyed_dictmapping_spec:Type[_KT]Callable[[_KT],_VT], *ignore_unpopulated_attribute: bool = False Type[KeyFuncDict[_KT _KT]


一种基于字典的集合类型,具有基于列的键控。


在 2.0 版更改: 已将 column_mapped_collection 重命名为 column_keyed_dict


返回一个 KeyFuncDict 工厂,该工厂将根据要添加到字典的 ORM 映射实例上的特定 Column-mapped 属性的值生成新的字典键。


注意


在将对象添加到 Dictionary 集合时,必须为 target 属性的值分配其值。此外,不会跟踪对 key 属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关更多详细信息,请参阅 处理密钥更改和反向填充 Dictionary 集合


另请参阅


词典集合 - 使用背景


参数

  • mapping_spec– 一个 Column 对象,目标映射器希望将其映射到映射类上的特定属性,该对象在特定实例上的值将用作该实例的新字典条目的键。

  • ignore_unpopulated_attribute


    如果为 True,并且对象上给定的 Column target 属性指示的映射属性根本未填充,则将以静默方式跳过该作。默认情况下,会引发错误。


    2.0 版本的新Function:如果属性 用于 Dictionary 键时,确定它从未 填充任何值。 这 column_keyed_dict.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。 这与 1.x 系列的行为形成鲜明对比,后者 错误地使用任意键填充字典中的值 值为 None


函数 sqlalchemy.orm 中。keyfunc_mappingkeyfunc Callable[[Any]Any] *ignore_unpopulated_attribute: bool = false type[KeyFuncDict[_KT, any]


具有任意键控的基于字典的集合类型。


在 2.0 版更改: 已将 mapped_collection 重命名为 keyfunc_mapping() 中。


返回一个 KeyFuncDict 工厂,其中包含从 keyfunc 生成的键控函数,该函数采用实体并返回键值。


注意


在将目标对象添加到集合时,仅调用给定的 keyfunc 一次。不会跟踪对函数返回的有效值的更改。


另请参阅


词典集合 - 使用背景


参数

  • keyfunc—— 一个可调用对象,它将传递 ORM 映射的实例,然后该实例应该生成一个新键以在字典中使用。如果返回的值为 LoaderCallableStatus.NO_VALUE,则会引发错误。

  • ignore_unpopulated_attribute


    if True,则可调用对象返回 LoaderCallableStatus.NO_VALUE对于特定实例,该作将被静默跳过。默认情况下,会引发错误。


    2.0 新版功能: 如果 callable being used for the dictionary key 返回 LoaderCallableStatus.NO_VALUE,它在 ORM 属性上下文中表示从未填充过任何值的属性。这 mapped_collection.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。这是 与 1.x 系列的行为相反,1.x 系列会错误地 使用任意键值


sqlalchemy.orm 中。 attribute_mapped_collection = <函数 attribute_keyed_dict>


基于字典的集合类型,具有基于属性的键控。


在 2.0 版更改: 已将 attribute_mapped_collection 重命名为 attribute_keyed_dict() 中。


返回一个 KeyFuncDict 工厂,该工厂将根据要添加到字典的 ORM 映射实例上的特定命名属性的值生成新的字典键。


注意


在将对象添加到 Dictionary 集合时,必须为 target 属性的值分配其值。此外,不会跟踪对 key 属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关更多详细信息,请参阅 处理密钥更改和反向填充 Dictionary 集合


另请参阅


词典集合 - 使用背景


参数

  • attr_name – 映射类上 ORM 映射属性的字符串名称,该属性在特定实例上的值将用作该实例的新字典条目的键。

  • ignore_unpopulated_attribute


    如果为 True,并且对象的 target 属性根本未填充,则将以静默方式跳过该作。默认情况下,会引发错误。


    2.0 版本的新Function:如果属性 用于 Dictionary 键时,确定它从未 填充任何值。 这 attribute_keyed_dict.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。 这与 1.x 系列的行为形成鲜明对比,后者 错误地使用任意键填充字典中的值 值为 None


sqlalchemy.orm 中。 column_mapped_collection = <函数 column_keyed_dict>


一种基于字典的集合类型,具有基于列的键控。


在 2.0 版更改: 已将 column_mapped_collection 重命名为 column_keyed_dict


返回一个 KeyFuncDict 工厂,该工厂将根据要添加到字典的 ORM 映射实例上的特定 Column-mapped 属性的值生成新的字典键。


注意


在将对象添加到 Dictionary 集合时,必须为 target 属性的值分配其值。此外,不会跟踪对 key 属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关更多详细信息,请参阅 处理密钥更改和反向填充 Dictionary 集合


另请参阅


词典集合 - 使用背景


参数

  • mapping_spec – 一个 Column 对象,目标映射器希望将其映射到映射类上的特定属性,该对象在特定实例上的值将用作该实例的新字典条目的键。

  • ignore_unpopulated_attribute


    如果为 True,并且对象上给定的 Column target 属性指示的映射属性根本未填充,则将以静默方式跳过该作。默认情况下,会引发错误。


    2.0 版本的新Function:如果属性 用于 Dictionary 键时,确定它从未 填充任何值。 这 column_keyed_dict.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。 这与 1.x 系列的行为形成鲜明对比,后者 错误地使用任意键填充字典中的值 值为 None


sqlalchemy.orm 中。 mapped_collection = <函数 keyfunc_mapping>


具有任意键控的基于字典的集合类型。


在 2.0 版更改: 已将 mapped_collection 重命名为 keyfunc_mapping() 中。


返回一个 KeyFuncDict 工厂,其中包含从 keyfunc 生成的键控函数,该函数采用实体并返回键值。


注意


在将目标对象添加到集合时,仅调用给定的 keyfunc 一次。不会跟踪对函数返回的有效值的更改。


另请参阅


词典集合 - 使用背景


参数

  • keyfunc – 一个可调用对象,它将传递 ORM 映射的实例,然后该实例应生成一个新键以在字典中使用。如果返回的值为 LoaderCallableStatus.NO_VALUE,则会引发错误。

  • ignore_unpopulated_attribute


    if True,则可调用对象返回 LoaderCallableStatus.NO_VALUE对于特定实例,该作将被静默跳过。默认情况下,会引发错误。


    2.0 新版功能: 如果 callable being used for the dictionary key 返回 LoaderCallableStatus.NO_VALUE,它在 ORM 属性上下文中表示从未填充过任何值的属性。这 mapped_collection.ignore_unpopulated_attribute 参数,这将指示此条件 应忽略,并静默跳过 append作。这是 与 1.x 系列的行为相反,1.x 系列会错误地 使用任意键值


sqlalchemy.orm 中。KeyFuncDict


ORM 映射字典类的 Base。


使用 SQLAlchemy ORM 集合类所需的其他方法扩展 dict 类型。使用 KeyFuncDict 最直接的方式是使用 attribute_keyed_dict()column_keyed_dict() 类工厂。 KeyFuncDict 还可以用作用户定义的自定义字典类的基础。


在 2.0 版更改: 已将 MappedCollection 重命名为 KeyFuncDict 的 API 函数。


类签名


class sqlalchemy.orm.KeyFuncDictbuiltins.dicttyping.通用


方法 sqlalchemy.orm.KeyFuncDict 中。__init__keyfunc Callable[[Any]Any] *dict_args Anyignore_unpopulated_属性 bool = False


使用 keyfunc 提供的键控创建新集合。


keyfunc 可以是任何接受对象并返回对象以用作字典键的可调用对象。


每次 ORM 需要添加成员时,都会调用 keyfunc value-only (例如,从数据库加载实例时)或 删除成员。 关于字典键控的常见注意事项适用 - keyfunc(object) 应该在集合的生命周期内返回相同的输出。基于可变属性的键控可能会导致无法访问的实例在集合中 “丢失”。


方法 sqlalchemy.orm.KeyFuncDict 中。clear None。  从 D 中删除所有项目

方法 sqlalchemy.orm.KeyFuncDict 中。popk[d] v,去掉指定的 key 返回 对应的值。


如果未找到 key,则返回 default(如果给定);否则,引发 KeyError。


方法 sqlalchemy.orm.KeyFuncDict 中。popitem


删除并返回 (key, value) 对作为 2 元组。


对以 LIFO (后进先出) 顺序返回。如果 dict 为空,则引发 KeyError。


方法 sqlalchemy.orm.KeyFuncDict 中。removevalue _KT, _sa_initiator:AttributeEventTokenLiteral[None,False]=None None


按值删除项目,并查阅 keyfunc 以获取键。


方法 sqlalchemy.orm.KeyFuncDict 中。setvalue _KT, _sa_initiator:AttributeEventTokenLiteral[None,False]=None


按值添加项目,查阅 keyfunc 以获取 key。


方法 sqlalchemy.orm.KeyFuncDict 中。setdefaultkeydefault=None


如果 key 不在字典中,则插入值为 default 的 key。


如果 key 在字典中,则返回 key 的值,否则返回 default。


方法 sqlalchemy.orm.KeyFuncDict 中。update[E]**F None.  dict/iterable E F 更新 D


如果 E 存在并且有一个 .keys() 方法,则执行: 对于 E 中的 k: D[k] = E[k] 如果 E 存在并且缺少 .keys() 方法,则执行: 对于 k,E 中的 v: D[k] = v 在任何一种情况下,后跟: 对于 F 中的 k: D[k] = F[k]


sqlalchemy.orm 中。MappedCollection = <类 'sqlalchemy.orm.mapped_collection.KeyFuncDict '>


ORM 映射字典类的 Base。


使用 SQLAlchemy ORM 集合类所需的其他方法扩展 dict 类型。使用 KeyFuncDict 最直接的方式是使用 attribute_keyed_dict()column_keyed_dict() 类工厂。 KeyFuncDict 还可以用作用户定义的自定义字典类的基础。


在 2.0 版更改: 已将 MappedCollection 重命名为 KeyFuncDict 的 API 函数。


集合内部


对象名称

描述


bulk_replace(值、existing_adapter、new_adapter[、发起方])


加载新集合,根据之前的 like 成员资格触发事件。


收集


实体集合类的装饰器。

collection_adapter


attrgetter(attr, ...) –> attrgetter 对象


CollectionAdapter 系列


ORM 和任意 Python 集合之间的桥梁。


InstrumentedDict (检测字典)


内置 dict 的插桩版本。


InstrumentedList 检测列表


内置列表的插桩版本。


插桩集


内置集的插桩版本。


prepare_instrumentation(工厂)


准备一个 callable 以备将来用作集合类工厂。


函数 sqlalchemy.orm.collections 中。bulk_replaceexisting_adapternew_adapter发起方=


加载新集合,根据之前的 like 成员资格触发事件。


values 中的实例附加到 new_adapter。将为 existing_adapter 中不存在的任何实例触发事件。值中不存在existing_adapter的任何实例都将触发 remove 事件。


参数

sqlalchemy.orm.collections 中。集合


实体集合类的装饰器。


装饰器分为两组:annotations 和 interception recipes。


注释装饰器(appender、remover、iterator、converter internally_instrumented)指示方法的用途,并且不接受任何参数。它们不是用括号编写的:

@collection.appender
def append(self, append): ...


配方装饰器都需要括号,即使是那些不带参数的:

@collection.adds("entity")
def insert(self, position, entity): ...


@collection.removes_return()
def popitem(self): ...

method sqlalchemy.orm.collections.collection. static addsarg


将方法标记为将实体添加到集合中。


向方法添加“添加到集合”处理。装饰器参数指示哪个方法参数持有 SQLAlchemy 相关值。参数可以按位置(即整数)或名称指定:

@collection.adds(1)
def push(self, item): ...


@collection.adds("entity")
def do_stuff(self, thing, entity=None): ...

方法 sqlalchemy.orm.collections.collection. static appenderfn


将方法标记为集合附加程序。


appender 方法使用一个位置参数调用:要 append 的值。如果还没有装饰,该方法将自动用 'adds(1)' 装饰:

@collection.appender
def add(self, append): ...


# or, equivalently
@collection.appender
@collection.adds(1)
def add(self, append): ...


# for mapping type, an 'append' may kick out a previous value
# that occupies that slot.  consider d['a'] = 'foo'- any previous
# value in d['a'] is discarded.
@collection.appender
@collection.replaces(1)
def add(self, entity):
    key = some_key_func(entity)
    previous = None
    if key in self:
        previous = self[key]
    self[key] = entity
    return previous


如果集合中不允许 add 的值,则可以引发异常。要记住的是,将为数据库查询映射的每个对象调用 appender。如果数据库包含违反集合语义的行,您将需要发挥创意来解决问题,因为通过集合访问将不起作用。


如果 appender 方法是内部检测的,则还必须接收关键字参数 '_sa_initiator' 并确保其向集合事件发布。


方法 sqlalchemy.orm.collections.collection. Static ConverterFN


将方法标记为集合转换器。


1.3 版后已移除: collection.converter() 处理程序已弃用,并将在未来版本中删除。请结合 listen() 函数参考 bulk_replace 侦听器接口。


当一个集合被完全替换时,将调用这个可选方法,如下所示:

myobj.acollection = [newvalue1, newvalue2]


converter 方法将接收被分配的对象,并应返回适合 appender 使用的值的可迭代对象 方法。 转换器不得分配值或更改集合, 它的唯一工作是将用户提供的值调整为 iterable 供 ORM 使用的值。


默认的 converter 实现将使用 duck-typing 进行转换。类似 dict 的集合将被转换为字典值的可迭代对象,而其他类型将简单地迭代:

@collection.converter
def convert(self, other): ...


如果对象的 duck-typing 与此集合的类型不匹配,则会引发 TypeError。


如果要扩展可批量分配的可能类型的范围,或对即将分配的值执行验证,请提供此方法的实现。


方法 sqlalchemy.orm.collections.collection. static internally_instrumentedfn


将方法标记为 instrumented。


此标签将阻止将任何装饰应用于 方法。如果要编排自己的对 collection_adapter() 在基本的 SQLAlchemy 接口方法之一中,或者防止自动 ABC 方法修饰包装您的实现:

# normally an 'extend' method on a list-like class would be
# automatically intercepted and re-implemented in terms of
# SQLAlchemy events and append().  your implementation will
# never be called, unless:
@collection.internally_instrumented
def extend(self, items): ...

方法 sqlalchemy.orm.collections.collection. static iteratorfn


将方法标记为集合删除程序。


调用 iterator 方法时不带任何参数。它应返回所有集合成员的迭代器:

@collection.iterator
def __iter__(self): ...

方法 sqlalchemy.orm.collections.collection. Static removerfn


将方法标记为集合删除程序。


remover 方法使用一个位置参数调用:值 以删除。该方法将自动使用 removes_return() 如果尚未装饰:

@collection.remover
def zap(self, entity): ...


# or, equivalently
@collection.remover
@collection.removes_return()
def zap(self): ...


如果要删除的值在集合中不存在,则可以引发异常或返回 None 以忽略错误。


如果 remove 方法是内部插桩的,则还必须接收关键字参数 '_sa_initiator' 并确保将其颁布到集合事件。


method sqlalchemy.orm.collections.collection. static removesarg


将方法标记为删除集合中的实体。


向方法添加“从集合中删除”处理。装饰器参数指示哪个方法参数包含要删除的 SQLAlchemy 相关值。参数可以按位置(即整数)或名称指定:

@collection.removes(1)
def zap(self, item): ...


对于在调用时不知道要删除的值的方法,请使用 collection.removes_return。


method sqlalchemy.orm.collections.collection. static removes_return


将方法标记为删除集合中的实体。


向方法添加“从集合中删除”处理。方法的返回值(如果有)被视为要删除的值。不检查方法参数:

@collection.removes_return()
def pop(self): ...


对于在调用时要删除的值已知的方法,请使用 collection.remove。


method sqlalchemy.orm.collections.collection. static replacesarg


将方法标记为替换集合中的实体。


向方法添加“添加到集合”和“从集合中删除”处理。装饰器参数指示哪个方法参数包含要添加的 SQLAlchemy 相关值,并返回值(如果有)将被视为要删除的值。


参数可以按位置(即整数)或名称指定:

@collection.replaces(2)
def __setitem__(self, index, item): ...

sqlalchemy.orm.collections 的 collection_adapter = operator.attrgetter('_sa_adapter')¶


attrgetter(attr, ...) –> attrgetter 对象


返回一个可调用对象,该对象从其作数中获取给定的属性。在 f = attrgetter('name') 之后,调用 f(r) 返回 r.name。在 g = attrgetter('name', 'date') 之后,调用 g(r) 返回 (r.name, r.date)。在 h = attrgetter('name.first', 'name.last') 之后,调用 h(r) 返回 (r.name.first, r.name.last)。


sqlalchemy.orm.collections 中。CollectionAdapter(集合适配器)¶


ORM 和任意 Python 集合之间的桥梁。


将基本级别的集合作(追加、删除、迭代)代理到底层 Python 集合,并为进入或离开集合的实体发出添加/删除事件。


ORM 专门使用 CollectionAdapter 与实体集合进行交互。


sqlalchemy.orm.collections 中。InstrumentedDict


内置 dict 的插桩版本。


类签名


class sqlalchemy.orm.collections.InstrumentedDict builtins.dicttyping.通用


sqlalchemy.orm.collections 中。InstrumentedList(插桩列表)¶


内置列表的插桩版本。


类签名


class sqlalchemy.orm.collections.InstrumentedList builtins.listtyping.通用


sqlalchemy.orm.collections 中。InstrumentedSet(插桩集)¶


内置集的插桩版本。


类签名


class sqlalchemy.orm.collections.InstrumentedSet builtins.settyping.通用


函数 sqlalchemy.orm.collections 中。prepare_instrumentationfactory:Type[Collection[Any]]]可调用[[],_AdaptedCollectionProtocol] Callable[[] _AdaptedCollectionProtocol]


准备一个 callable 以备将来用作集合类工厂。


给定一个集合类 factory(类型或无参数可调用对象),返回另一个工厂,该工厂在调用时将产生兼容的实例。


此函数负责将 collection_class=list 转换为 collection_class=InstrumentedList 的运行时行为。