SqlAlchemy 2.0 中文文档(十四)(1)https://developer.aliyun.com/article/1562969
自定义基于字典的集合
KeyFuncDict
类可用作自定义类型的基类,也可以用作快速将dict
集合支持添加到其他类的混合。它使用键函数来委托给__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__()
,则用户定义的版本应当被装饰 collection.internally_instrumented()
,如果 它们在 KeyFuncDict
上调用这些方法。因为 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 理解 dict
接口就像列表和集合一样,并且如果选择子类化 dict
或在鸭子类型类中提供类似于 dict 的集合行为,则会自动为所有“类似于字典”的方法进行仪器化。然而,您必须装饰添加器和删除器方法-因为 SQLAlchemy 没有默认使用的基本字典接口的兼容方法。迭代将通过 values()
进行,除非另有装饰。
仪器化和自定义类型
许多自定义类型和现有库类可以直接用作实体集合类型而无需进一步操作。但是,重要的是要注意,仪器化过程将修改类型,自动在方法周围添加装饰器。
这些装饰很轻量级,在关系之外是无操作的,但是在其他地方触发时会增加不必要的开销。当将库类用作集合时,将装饰限制为仅在关系中使用的“简单子类”技巧是一个好习惯。例如:
class MyAwesomeList(some.great.library.AwesomeList): pass # ... relationship(..., collection_class=MyAwesomeList)
ORM 对内置类型使用此方法,当直接使用 list
、set
或 dict
时,会悄悄地替换为一个简单的子类。
集合 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 映射字典类的基类。 |
mapped_collection | 基于字典的集合类型,具有任意的键。 |
MappedCollection | ORM 映射字典类的基类。 |
function sqlalchemy.orm.attribute_keyed_dict(attr_name: str, *, ignore_unpopulated_attribute: bool = False) → Type[KeyFuncDict[Any, Any]]
基于属性键的字典类型的集合。
版本 2.0 中的更改:将attribute_mapped_collection
重命名为attribute_keyed_dict()
。
返回一个KeyFuncDict
工厂,它将根据 ORM 映射实例上的特定命名属性的值生成新的字典键,以添加到字典中。
注意
目标属性的值必须在将对象添加到字典集合时被赋予其值。另外,不会跟踪键属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关详细信息,请参阅处理键变异和反向填充字典集合。
另请参见
字典集合 - 使用背景
参数:
attr_name
- 映射类上的 ORM 映射属性的字符串名称,其值将在特定实例上用作新字典条目的键。ignore_unpopulated_attribute
-
如果为 True,并且对象上的目标属性根本未填充,则操作将被静默跳过。默认情况下,会引发错误。
版本 2.0 中的新特性:如果确定用于字典键的属性从未填充过任何值,则默认会引发错误。可以设置attribute_keyed_dict.ignore_unpopulated_attribute
参数,该参数将指示忽略此条件,并在静默跳过附加操作。这与 1.x 系列的行为相反,后者会错误地使用任意键值None
填充字典中的值。
function sqlalchemy.orm.column_keyed_dict(mapping_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
映射属性的值生成新的字典键,以添加到字典中。
注意
目标属性的值必须在将对象添加到字典集合时分配其值。此外,不会跟踪键属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关详细信息,请参见处理键突变和为字典集合回填。
另请参阅
字典集合 - 使用背景
参数:
mapping_spec
- 一个预期由目标映射器映射到映射类上特定属性的Column
对象,其在特定实例上的值将用作该实例的新字典条目的键。ignore_unpopulated_attribute
-
如果为 True,并且对象上由给定Column
目标属性指示的映射属性根本未填充,则操作将被静默跳过。默认情况下,会引发错误。
2.0 版本中的新功能:如果确定用于字典键的属性从未填充任何值,则默认情况下会引发错误。可以设置column_keyed_dict.ignore_unpopulated_attribute
参数,该参数将指示应忽略此条件,并且附加操作将被静默跳过。这与 1.x 系列的行为相反,后者会错误地使用任意键值None
填充字典中的值。
function sqlalchemy.orm.keyfunc_mapping(keyfunc: _F, *, ignore_unpopulated_attribute: bool = False) → Type[KeyFuncDict[_KT, Any]]
基于字典的集合类型,具有任意键。
2.0 版本中的更改:将mapped_collection
重命名为keyfunc_mapping()
。
返回一个从 keyfunc 生成的键函数的KeyFuncDict
工厂,一个可调用对象,接受一个实体并返回一个键值。
注意
给定的 keyfunc 仅在将目标对象添加到集合时调用一次。不会跟踪函数返回的有效值的更改。
另请参阅
字典集合 - 使用背景
参数:
keyfunc
- 一个可调用对象,将传递 ORM 映射的实例,然后生成一个用于字典中的新键。如果返回的值是LoaderCallableStatus.NO_VALUE
,则会引发错误。ignore_unpopulated_attribute
-
如果为 True,并且可调用函数对特定实例返回LoaderCallableStatus.NO_VALUE
,则操作将被静默跳过。默认情况下会引发错误。
2.0 版本中的新功能:如果用于字典键的可调用函数返回LoaderCallableStatus.NO_VALUE
,则默认情况下会引发错误,这在 ORM 属性上下文中表示从未填充任何值的属性。可以设置mapped_collection.ignore_unpopulated_attribute
参数,该参数将指示应忽略此条件,并且附加操作将被静默跳过。这与 1.x 系列的行为相反,后者会错误地使用任意键值None
填充字典中的值。
sqlalchemy.orm.attribute_mapped_collection = <function attribute_keyed_dict>
基于属性的键的字典集合类型。
2.0 版本中的更改:将attribute_mapped_collection
重命名为attribute_keyed_dict()
。
返回一个KeyFuncDict
工厂,该工厂将根据 ORM 映射实例上特定命名属性的值生成新的字典键,以添加到字典中。
注意
目标属性的值必须在将对象添加到字典集合时分配其值。此外,不会跟踪键属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。有关详细信息,请参阅处理键突变和为字典集合回填。
另请参见
字典集合 - 使用背景
参数:
attr_name
– 映射类上 ORM 映射属性的字符串名称,特定实例上的该值将用作该实例的新字典条目的键。ignore_unpopulated_attribute
–
如果为 True,并且对象上的目标属性根本未填充,则操作将被静默跳过。默认情况下会引发错误。
2.0 版新功能:默认情况下,如果确定用于字典键的属性从未被填充任何值,则将引发错误。可以设置attribute_keyed_dict.ignore_unpopulated_attribute
参数,以指示应忽略此条件,并静默跳过追加操作。这与 1.x 系列的行为相反,后者将错误地使用任意键值None
填充字典中的值。
sqlalchemy.orm.column_mapped_collection = <function column_keyed_dict>
基于字典的集合类型,使用列作为键。
2.0 版更改:将 column_mapped_collection
重命名为 column_keyed_dict
。
返回一个 KeyFuncDict
工厂,它将根据 ORM 映射实例上的特定 Column
映射的属性的值产生新的字典键,并将其添加到字典中。
注意
目标属性的值必须在将对象添加到字典集合时被赋值。另外,不会跟踪键属性的更改,这意味着字典中的键不会自动与目标对象本身的键值同步。参见处理键变化和字典集合的反填充获取更多详细信息。
另见
字典集合 - 使用背景
参数:
mapping_spec
– 一个预期由目标映射器映射到映射类上特定属性的Column
对象,其在特定实例上的值将用作该实例的新字典条目的键。ignore_unpopulated_attribute
–
如果为 True,并且对象上由给定Column
目标属性指示的映射属性根本未被填充,则操作将被静默跳过。默认情况下,将引发错误。
2.0 版新功能:默认情况下,如果确定用于字典键的属性从未被填充任何值,则将引发错误。可以设置column_keyed_dict.ignore_unpopulated_attribute
参数,以指示应忽略此条件,并静默跳过追加操作。这与 1.x 系列的行为相反,后者将错误地使用任意键值None
填充字典中的值。
sqlalchemy.orm.mapped_collection = <function keyfunc_mapping>
一种基于字典的集合类型,具有任意键。
从版本 2.0 开始更改:将mapped_collection
重命名为keyfunc_mapping()
。
返回一个KeyFuncDict
工厂,其中包含从 keyfunc 生成的键函数,一个接受实体并返回键值的可调用对象。
注意
给定的 keyfunc 仅在将目标对象添加到集合时调用一次。不会跟踪函数返回的有效值的更改。
另请参见
字典集合 - 使用背景
参数:
keyfunc
– 一个可调用对象,将传递给 ORM 映射的实例,然后生成一个新的键用于字典。如果返回的值是LoaderCallableStatus.NO_VALUE
,则会引发错误。ignore_unpopulated_attribute
–
如果为 True,并且可调用对象对于特定实例返回LoaderCallableStatus.NO_VALUE
,则操作将被静默跳过。默认情况下,会引发错误。
从版本 2.0 开始:如果用于字典键的可调用对象返回LoaderCallableStatus.NO_VALUE
,则默认情况下会引发错误,这在 ORM 属性上下文中表示从未用任何值填充的属性。可以设置mapped_collection.ignore_unpopulated_attribute
参数,该参数将指示应忽略此条件,并且附加操作将被静默跳过。这与 1.x 系列的行为相反,后者会错误地使用任意键值None
填充字典中的值。
class sqlalchemy.orm.KeyFuncDict
ORM 映射字典类的基础。
使用额外方法扩展了dict
类型,这些方法是 SQLAlchemy ORM 集合类所需的。最直接使用attribute_keyed_dict()
或column_keyed_dict()
类工厂来使用KeyFuncDict
。KeyFuncDict
也可以作为用户定义的自定义字典类的基础。
从版本 2.0 开始更改:将MappedCollection
重命名为KeyFuncDict
。
另请参见
attribute_keyed_dict()
column_keyed_dict()
字典集合
自定义集合实现
成员
init(), clear(), pop(), popitem(), remove(), set(), setdefault(), update()
类签名
类 sqlalchemy.orm.KeyFuncDict
(builtins.dict
, typing.Generic
)
method __init__(keyfunc: _F, *dict_args: Any, ignore_unpopulated_attribute: bool = False) → None
使用 keyfunc 提供的键创建一个新的集合。
keyfunc 可以是任何接受对象并返回用作字典键的对象的可调用对象。
每次 ORM 需要按值添加成员(例如从数据库加载实例时)或移除成员时都会调用 keyfunc。通常的字典键警告适用- keyfunc(object)
应该在集合的生命周期内返回相同的输出。基于可变属性的键可能会导致集合中“丢失”的不可达实例。
method clear() → None. Remove all items from D.
method pop(k[, d]) → v, remove specified key and return the corresponding value.
如果未找到键,则返回给定的默认值;否则,引发 KeyError。
method popitem()
移除并返回一个(键,值)对作为 2 元组。
对中的对以 LIFO(后进先出)顺序返回。如果字典为空,则引发 KeyError。
method remove(value: _KT, _sa_initiator: AttributeEventToken | Literal[None, False] = None) → None
通过值删除项,查询 keyfunc 以获取键。
method set(value: _KT, _sa_initiator: AttributeEventToken | Literal[None, False] = None) → None
通过值添加项,查询 keyfunc 以获取键。
method setdefault(key, default=None)
如果键不在字典中,则将键插入并将默认值设置为默认值。
如果键在字典中,则返回键的值,否则返回默认值。
method update([E, ]**F) → None. Update D from dict/iterable E and F.
如果 E 存在并且具有 .keys() 方法,则执行以下操作:for k in E: D[k] = E[k] 如果 E 存在并且缺少 .keys() 方法,则执行以下操作:for k, v in E: D[k] = v 在任一情况下,这之后都会执行:for k in F: D[k] = F[k]
sqlalchemy.orm.MappedCollection = <class 'sqlalchemy.orm.mapped_collection.KeyFuncDict'>
ORM 映射字典类的基础。
扩展了 dict
类型,提供了 SQLAlchemy ORM 集合类所需的附加方法。最直接使用 attribute_keyed_dict()
或 column_keyed_dict()
类工厂创建 KeyFuncDict
。KeyFuncDict
也可以作为用户定义的自定义字典类的基类。
在 2.0 版本中更改:将 MappedCollection
重命名为 KeyFuncDict
。
另请参阅
attribute_keyed_dict()
column_keyed_dict()
字典集合
自定义集合实现
集合内部
对象名称 | 描述 |
bulk_replace(values, existing_adapter, new_adapter[, initiator]) | 加载一个新的集合,根据先前的成员关系触发事件。 |
collection | 实体集合类的装饰器。 |
collection_adapter | attrgetter(attr, …) –> attrgetter 对象 |
CollectionAdapter | ORM 和任意 Python 集合之间的桥梁。 |
InstrumentedDict | 内置字典的受控版本。 |
InstrumentedList | 内置列表的受控版本。 |
InstrumentedSet | 内置集合的受控版本。 |
prepare_instrumentation(factory) | 为将来用作集合类工厂的可调用对象做准备。 |
function sqlalchemy.orm.collections.bulk_replace(values, existing_adapter, new_adapter, initiator=None)
加载一个新的集合,根据先前的成员关系触发事件。
将values
中的实例附加到new_adapter
。对于existing_adapter
中不存在的任何实例,将触发事件。对于existing_adapter
中存在但在values
中不存在的任何实例,将触发删除事件。
参数:
values
– 一个集合成员实例的可迭代对象existing_adapter
– 要替换的实例的CollectionAdapter
new_adapter
– 一个空的CollectionAdapter
,用于加载values
class sqlalchemy.orm.collections.collection
实体集合类的装饰器。
这些装饰器分为两组:注释和拦截配方。
标注装饰器(appender, remover, iterator, converter, internally_instrumented)指示方法的目的,并且不接受任何参数。它们不带括号:
@collection.appender def append(self, append): ...
所有配方装饰器都需要括号,即使没有参数:
成员
adds(), appender(), converter(), internally_instrumented(), iterator(), remover(), removes(), removes_return(), replaces()
@collection.adds('entity') def insert(self, position, entity): ... @collection.removes_return() def popitem(self): ...
method static adds(arg)
将方法标记为向集合添加实体。
将“添加到集合”处理添加到方法中。装饰器参数指示哪个方法参数保存着与 SQLAlchemy 相关的值。参数可以通过位置指定(即整数),也可以通过名称指定:
@collection.adds(1) def push(self, item): ... @collection.adds('entity') def do_stuff(self, thing, entity=None): ...
method static appender(fn)
将方法标记为集合附加器。
调用 appender 方法时,将使用一个位置参数:要附加的值。如果尚未装饰该方法,将自动用 ‘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
如果要附加的值不允许在集合中,您可以引发异常。需要记住的一件事是,对于由数据库查询映射的每个对象,都会调用 appender。如果数据库包含违反集合语义的行,则需要有创造性地解决问题,因为通过集合访问将无法正常工作。
如果 appender 方法在内部被仪器化,则还必须接收关键字参数 ‘_sa_initiator’ 并确保其在集合事件中的传播。
method static converter(fn)
将方法标记为集合转换器。
自版本 1.3 起弃用:collection.converter()
处理程序已弃用,并将在将来的版本中删除。请参考 listen()
函数结合 bulk_replace
监听器接口。
当要完全替换集合时,将调用此可选方法,例如:
myobj.acollection = [newvalue1, newvalue2]
转换器方法将接收到被分配的对象,并应返回适用于 appender
方法使用的值的可迭代对象。转换器不能分配值或改变集合,它的唯一工作是将用户提供的值适应为 ORM 使用的值的可迭代对象。
默认的转换器实现将使用鸭子类型进行转换。类似字典的集合将转换为字典值的可迭代对象,而其他类型将简单地进行迭代:
@collection.converter def convert(self, other): ...
如果对象的鸭子类型与此集合的类型不匹配,则会引发 TypeError。
如果要扩展可以批量分配的可能类型的范围,或者对即将被分配的值执行验证,请提供此方法的实现。
method static internally_instrumented(fn)
将方法标记为受仪器控制的。
此标记将阻止对该方法应用任何装饰。如果您正在编排自己对 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): ...
method static iterator(fn)
将方法标记为集合移除器。
调用 iterator 方法时不使用参数。预期它将返回所有集合成员的迭代器:
@collection.iterator def __iter__(self): ...
method static remover(fn)
将方法标记为集合移除器。
remover
方法接受一个位置参数:要移除的值。如果方法尚未装饰,则会自动装饰为 removes_return()
:
@collection.remover def zap(self, entity): ... # or, equivalently @collection.remover @collection.removes_return() def zap(self, ): ...
如果要移除的值在集合中不存在,则可以引发异常或返回 None 以忽略错误。
如果 remove 方法在内部被检测,则还必须接收关键字参数 ‘_sa_initiator’ 并确保其传播到集合事件。
method static removes(arg)
将该方法标记为从集合中移除实体。
将“从集合中移除”处理添加到方法中。装饰器参数指示哪个方法参数保存了要从 SQLAlchemy 中移除的值。参数可以通过位置(即整数)或名称指定:
@collection.removes(1) def zap(self, item): ...
对于在调用时不知道要移除的值的方法,请使用 collection.removes_return
。
method static removes_return()
将该方法标记为从集合中移除实体。
将“从集合中移除”处理添加到方法中。如果有,则方法的返回值将被视为要移除的值。不会检查方法参数:
@collection.removes_return() def pop(self): ...
对于在调用时知道要移除的值的方法,请使用 collection.remove
。
method static replaces(arg)
将该方法标记为替换集合中的实体。
将“添加到集合中”和“从集合中移除”处理添加到方法中。装饰器参数指示哪个方法参数保存了要添加到 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)
。
class sqlalchemy.orm.collections.CollectionAdapter
在 ORM 和任意 Python 集合之间架设桥梁。
将基本级别的集合操作(追加、移除、迭代)代理到底层的 Python 集合,并为进入或离开集合的实体发出添加/移除事件。
ORM 专门使用 CollectionAdapter
与实体集合交互。
class sqlalchemy.orm.collections.InstrumentedDict
内置字典的受检测版本。
类签名
sqlalchemy.orm.collections.InstrumentedDict
类(builtins.dict
, typing.Generic
)
class sqlalchemy.orm.collections.InstrumentedList
内置列表的受检测版本。
类签名
sqlalchemy.orm.collections.InstrumentedList
类(builtins.list
, typing.Generic
)
class sqlalchemy.orm.collections.InstrumentedSet
内置集合的受检测版本。
类签名
类 sqlalchemy.orm.collections.InstrumentedSet
(builtins.set
, typing.Generic
)
function sqlalchemy.orm.collections.prepare_instrumentation(factory: Type[Collection[Any]] | Callable[[], _AdaptedCollectionProtocol]) → Callable[[], _AdaptedCollectionProtocol]
准备一个可调用对象,以便将来用作集合类工厂。
给定一个集合类工厂(类型或无参数可调用对象),返回另一个工厂,当调用时将产生兼容的实例。
此函数负责将 collection_class=list 转换为 collection_class=InstrumentedList 的运行时行为。
SqlAlchemy 2.0 中文文档(十四)(3)https://developer.aliyun.com/article/1562971