Collection Customization and API Details

relationship() 関数は、2つのクラス間のリンクを定義します。リンクが1対多または多対多の関係を定義する場合、オブジェクトが読み込まれ操作されるときにPythonコレクションとして表されます。この節では、コレクションの設定とテクニックについての追加情報を示します。

Customizing Collection Access

一対多または多対多の関係をマッピングすると、親インスタンスの属性を介してアクセス可能な値のコレクションが生成されます。これらの一般的なコレクション型は listset の2つで、 Declarative マッピングでは、 Mapped を使用しますが、以下の Parent.children コレクションで示すように、 Mapped コンテナ内のコレクション型を使用して確立されます。ここでは 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 コレクションに示されている set の場合:

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"))

Note

Python 3.7または3.8を使用している場合

コレクションのアノテーションは typing.List または typing.Set を使用する必要があります。例えば、 Mapped[List["Child"]] または Mapped[Set["Child"]] です。 list および set の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()

imperative mappings や型指定されていないPythonコードを使用する場合など、 Mapped アノテーションなしでマッピングを使用する場合や、いくつかの特殊なケースでは、 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_class または Mapped がない場合、デフォルトのコレクション型は list です。

listset 組み込みコマンド以外にも、以下の Dictionary Collections で説明されている2種類の辞書がサポートされています。任意の可変シーケンス型をターゲットコレクションとして設定することもサポートされていますが、いくつかの設定手順が追加されています。これについては Custom Collection Implementations で説明されています。

Dictionary Collections

辞書をコレクションとして使用する場合は、もう少し詳細な情報が必要になります。これは、オブジェクトが常にリストとしてデータベースからロードされ、辞書を正しく生成するためにキー生成方法が利用可能でなければならないためです。 attribute_keyed_dict() 関数は、単純な辞書コレクションを実現する最も一般的な方法です。マップされたクラスの特定の属性をキーとして適用する辞書クラスを生成します。以下では、 Note 項目の辞書を含む Item クラスを Note.keyword 属性にマップします。 attribute_keyed_dict() を使用する場合、 Mapped アノテーションは、次の例に示すように、 KeyFuncDict または単に dict を使用して型指定できます。ただし、この場合、 attribute_keyed_dict() が適切にパラメータ化されるように、 relationship.collection_class パラメータが必要です:

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.keywordNote.text フィールドの最初の10文字のタプルとしてオブジェクトを確立するときに、オブジェクトに関する詳細または詳細の組み合わせをキーとして使用することができます。:

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

上記では、双方向の relationship.back_populates 設定を持つ Note.item 関係を追加しました。この逆の関係に割り当てると、 NoteItem.notes 辞書に追加され、キーが自動的に生成されます。:

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

その他の組み込み辞書型には column_keyed_dict() があります。これは Column オブジェクトが直接与えられた場合を除いて、 attribute_keyed_dict() とほとんど同じです:

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() は先に述べたように、通常は 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”拡張と組み合わされて、合理化された辞書ビューを生成します。例として Proxying to Dictionary Based Collectionscomposite_association_proxy を参照してください。

Dealing with Key Mutations and back-populating for Dictionary collections

attribute_keyed_dict() を使用すると、辞書の”キー”はターゲットオブジェクトの属性から取得されます。 このキーへの変更は追跡されません 。これは、キーが最初に使用されるときにキーが割り当てられなければならず、キーが変更されてもコレクションは変更されないことを意味します。これが問題になる典型的な例は、属性にマップされたコレクションを埋め込むためにバックレフに依存する場合です。次の場合を想定します:

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>}

逆参照がこの方法で使用されている場合は、 __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)

Custom Collection Implementations

コレクションに独自の型を使用することもできます。単純なケースでは、 listset から継承したり、独自の動作を追加したりするだけで十分です。その他のケースでは、コレクションがどのように動作するかをSQLAlchemyに詳細に伝えるために、特別なデコレータが必要になります。

SQLAlchemyのコレクションは、透過的に 実装 されます。実装とは、コレクションに対する通常の操作が追跡され、変更がフラッシュ時にデータベースに書き込まれることを意味します。さらに、コレクション操作は、何らかの追加手術が必要であることを示す イベント を発生させることができます。追加手術の例としては、親の Session に子項目を保存すること(つまり、 save-update カスケード)や、双方向の関係の状態を同期させることなどがあります。 (i.e. a backref()).

コレクションパッケージは、リスト、セット、およびディクテーションの基本的なインターフェイスを理解し、これらの組み込み型とそのサブクラスにインストルメンテーションを自動的に適用します。基本的なコレクションインターフェイスを実装するオブジェクト派生型は、ダックタイピングによって検出され、インスツルメントされます。:

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__ はミューテーターメソッドではないので、実装されませんし、 foo も実装されません。

Duck-typing (つまり当て推量)は、もちろん岩のように堅固なものではありませんので、 __emulates__ クラス属性を与えることで、実装しているインターフェースを明確にすることができます。:

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)

このクラスには append メソッドがあるので、Pythonの list (つまり list-like )に似ていますが、 __emulates__ 属性によって強制的に set として扱われます。 remove はsetインターフェイスの一部として認識され、実装されます。

しかし、このクラスはまだ完全には動作しません。SQLAlchemyで使用できるようにするには、少し接着剤が必要です。ORMは、コレクションのメンバを追加、削除、反復するためにどのメソッドを使用するかを知る必要があります。 listset のような型を使用する場合、適切なメソッドはよく知られており、存在する場合には自動的に使用されます。しかし、 set に似ているだけの上記のクラスは、期待される add メソッドを提供しないので、 add メソッドの代わりになるメソッドをORMに示す必要があります。この場合はデコレータ @collection. appender を使用します。これについては次のセクションで説明します。

Annotating Custom Collections via Decorators

デコレータを使用して、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__ は集合のデフォルトのメソッドで、削除と反復に使用されます。デフォルトのメソッドも変更できます。

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メソッドは、マップされたエンティティを単一の引数として呼び出されます。iteratorメソッドは引数なしで呼び出され、iteratorを返す必要があります。

Custom Dictionary-Based Collections

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 をサブクラス化したり、duck型のクラスでdictのようなコレクション動作を提供したりすると、自動的にすべての dictのような メソッドを実装します。ただし、appenderメソッドとremoverメソッドは修飾する必要があります。基本的な辞書インターフェイスには、SQLAlchemyがデフォルトで使用する互換性のあるメソッドはありません。特に修飾しない限り、反復は values() を通過します。

Instrumentation and Custom Types

多くのカスタム型や既存のライブラリクラスは、そのままでエンティティコレクション型として使用できます。ただし、インストルメンテーションプロセスによって型が変更され、メソッドの周りにデコレータが自動的に追加されることに注意してください。

デコレーションは軽量で、関係以外では機能しませんが、他の場所でトリガーされると不要なオーバーヘッドが追加されます。ライブラリクラスをコレクションとして使用する場合は、”些細なサブクラス”のトリックを使用して、デコレーションを関係での使用のみに制限することをお勧めします。次に例を示します。:

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

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

ORMは組み込みに対してこのアプローチを使用しており、 listset 、または dict が直接使用された場合に、些細なサブクラスを静かに置き換えます。

Collection API

Object Name Description

attribute_keyed_dict(attr_name, *, [ignore_unpopulated_attribute])

A dictionary-based collection type with attribute-based keying.

attribute_mapped_collection

A dictionary-based collection type with attribute-based keying.

column_keyed_dict(mapping_spec, *, [ignore_unpopulated_attribute])

A dictionary-based collection type with column-based keying.

column_mapped_collection

A dictionary-based collection type with column-based keying.

keyfunc_mapping(keyfunc, *, [ignore_unpopulated_attribute])

A dictionary-based collection type with arbitrary keying.

KeyFuncDict

Base for ORM mapped dictionary classes.

mapped_collection

A dictionary-based collection type with arbitrary keying.

MappedCollection

Base for ORM mapped dictionary classes.

function sqlalchemy.orm.attribute_keyed_dict(attr_name: str, *, ignore_unpopulated_attribute: bool = False) Type[KeyFuncDict[Any, Any]]

A dictionary-based collection type with attribute-based keying.

Changed in version 2.0: Renamed attribute_mapped_collection to attribute_keyed_dict().

Returns a KeyFuncDict factory which will produce new dictionary keys based on the value of a particular named attribute on ORM mapped instances to be added to the dictionary.

Note

the value of the target attribute must be assigned with its value at the time that the object is being added to the dictionary collection. Additionally, changes to the key attribute are not tracked, which means the key in the dictionary is not automatically synchronized with the key value on the target object itself. See Dealing with Key Mutations and back-populating for Dictionary collections for further details.

See also

Dictionary Collections - background on use

Parameters:
  • attr_name – string name of an ORM-mapped attribute on the mapped class, the value of which on a particular instance is to be used as the key for a new dictionary entry for that instance.

  • ignore_unpopulated_attribute

    if True, and the target attribute on an object is not populated at all, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the attribute being used for the dictionary key is determined that it was never populated with any value. The attribute_keyed_dict.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

function sqlalchemy.orm.column_keyed_dict(mapping_spec: Type[_KT] | Callable[[_KT], _VT], *, ignore_unpopulated_attribute: bool = False) Type[KeyFuncDict[_KT, _KT]]

A dictionary-based collection type with column-based keying.

Changed in version 2.0: Renamed column_mapped_collection to column_keyed_dict.

Returns a KeyFuncDict factory which will produce new dictionary keys based on the value of a particular Column-mapped attribute on ORM mapped instances to be added to the dictionary.

Note

the value of the target attribute must be assigned with its value at the time that the object is being added to the dictionary collection. Additionally, changes to the key attribute are not tracked, which means the key in the dictionary is not automatically synchronized with the key value on the target object itself. See Dealing with Key Mutations and back-populating for Dictionary collections for further details.

See also

Dictionary Collections - background on use

Parameters:
  • mapping_spec – a Column object that is expected to be mapped by the target mapper to a particular attribute on the mapped class, the value of which on a particular instance is to be used as the key for a new dictionary entry for that instance.

  • ignore_unpopulated_attribute

    if True, and the mapped attribute indicated by the given Column target attribute on an object is not populated at all, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the attribute being used for the dictionary key is determined that it was never populated with any value. The column_keyed_dict.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

function sqlalchemy.orm.keyfunc_mapping(keyfunc: Callable[[Any], Any], *, ignore_unpopulated_attribute: bool = False) Type[KeyFuncDict[_KT, Any]]

A dictionary-based collection type with arbitrary keying.

Changed in version 2.0: Renamed mapped_collection to keyfunc_mapping().

Returns a KeyFuncDict factory with a keying function generated from keyfunc, a callable that takes an entity and returns a key value.

Note

the given keyfunc is called only once at the time that the target object is being added to the collection. Changes to the effective value returned by the function are not tracked.

See also

Dictionary Collections - background on use

Parameters:
  • keyfunc – a callable that will be passed the ORM-mapped instance which should then generate a new key to use in the dictionary. If the value returned is LoaderCallableStatus.NO_VALUE, an error is raised.

  • ignore_unpopulated_attribute

    if True, and the callable returns LoaderCallableStatus.NO_VALUE for a particular instance, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the callable being used for the dictionary key returns LoaderCallableStatus.NO_VALUE, which in an ORM attribute context indicates an attribute that was never populated with any value. The mapped_collection.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

sqlalchemy.orm.attribute_mapped_collection = <function attribute_keyed_dict>

A dictionary-based collection type with attribute-based keying.

Changed in version 2.0: Renamed attribute_mapped_collection to attribute_keyed_dict().

Returns a KeyFuncDict factory which will produce new dictionary keys based on the value of a particular named attribute on ORM mapped instances to be added to the dictionary.

Note

the value of the target attribute must be assigned with its value at the time that the object is being added to the dictionary collection. Additionally, changes to the key attribute are not tracked, which means the key in the dictionary is not automatically synchronized with the key value on the target object itself. See Dealing with Key Mutations and back-populating for Dictionary collections for further details.

See also

Dictionary Collections - background on use

Parameters:
  • attr_name – string name of an ORM-mapped attribute on the mapped class, the value of which on a particular instance is to be used as the key for a new dictionary entry for that instance.

  • ignore_unpopulated_attribute

    if True, and the target attribute on an object is not populated at all, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the attribute being used for the dictionary key is determined that it was never populated with any value. The attribute_keyed_dict.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

sqlalchemy.orm.column_mapped_collection = <function column_keyed_dict>

A dictionary-based collection type with column-based keying.

Changed in version 2.0: Renamed column_mapped_collection to column_keyed_dict.

Returns a KeyFuncDict factory which will produce new dictionary keys based on the value of a particular Column-mapped attribute on ORM mapped instances to be added to the dictionary.

Note

the value of the target attribute must be assigned with its value at the time that the object is being added to the dictionary collection. Additionally, changes to the key attribute are not tracked, which means the key in the dictionary is not automatically synchronized with the key value on the target object itself. See Dealing with Key Mutations and back-populating for Dictionary collections for further details.

See also

Dictionary Collections - background on use

Parameters:
  • mapping_spec – a Column object that is expected to be mapped by the target mapper to a particular attribute on the mapped class, the value of which on a particular instance is to be used as the key for a new dictionary entry for that instance.

  • ignore_unpopulated_attribute

    if True, and the mapped attribute indicated by the given Column target attribute on an object is not populated at all, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the attribute being used for the dictionary key is determined that it was never populated with any value. The column_keyed_dict.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

sqlalchemy.orm.mapped_collection = <function keyfunc_mapping>

A dictionary-based collection type with arbitrary keying.

Changed in version 2.0: Renamed mapped_collection to keyfunc_mapping().

Returns a KeyFuncDict factory with a keying function generated from keyfunc, a callable that takes an entity and returns a key value.

Note

the given keyfunc is called only once at the time that the target object is being added to the collection. Changes to the effective value returned by the function are not tracked.

See also

Dictionary Collections - background on use

Parameters:
  • keyfunc – a callable that will be passed the ORM-mapped instance which should then generate a new key to use in the dictionary. If the value returned is LoaderCallableStatus.NO_VALUE, an error is raised.

  • ignore_unpopulated_attribute

    if True, and the callable returns LoaderCallableStatus.NO_VALUE for a particular instance, the operation will be silently skipped. By default, an error is raised.

    New in version 2.0: an error is raised by default if the callable being used for the dictionary key returns LoaderCallableStatus.NO_VALUE, which in an ORM attribute context indicates an attribute that was never populated with any value. The mapped_collection.ignore_unpopulated_attribute parameter may be set which will instead indicate that this condition should be ignored, and the append operation silently skipped. This is in contrast to the behavior of the 1.x series which would erroneously populate the value in the dictionary with an arbitrary key value of None.

class sqlalchemy.orm.KeyFuncDict

Base for ORM mapped dictionary classes.

Extends the dict type with additional methods needed by SQLAlchemy ORM collection classes. Use of KeyFuncDict is most directly by using the attribute_keyed_dict() or column_keyed_dict() class factories. KeyFuncDict may also serve as the base for user-defined custom dictionary classes.

Changed in version 2.0: Renamed MappedCollection to KeyFuncDict.

Class signature

class sqlalchemy.orm.KeyFuncDict (builtins.dict, typing.Generic)

method sqlalchemy.orm.KeyFuncDict.__init__(keyfunc: Callable[[Any], Any], *dict_args: Any, ignore_unpopulated_attribute: bool = False) None

Create a new collection with keying provided by keyfunc.

keyfunc may be any callable that takes an object and returns an object for use as a dictionary key.

The keyfunc will be called every time the ORM needs to add a member by value-only (such as when loading instances from the database) or remove a member. The usual cautions about dictionary keying apply- keyfunc(object) should return the same output for the life of the collection. Keying based on mutable properties can result in unreachable instances “lost” in the collection.

method sqlalchemy.orm.KeyFuncDict.clear() None.  Remove all items from D.
method sqlalchemy.orm.KeyFuncDict.pop(k[, d]) v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

method sqlalchemy.orm.KeyFuncDict.popitem()

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

method sqlalchemy.orm.KeyFuncDict.remove(value: _KT, _sa_initiator: AttributeEventToken | Literal[None, False] = None) None

Remove an item by value, consulting the keyfunc for the key.

method sqlalchemy.orm.KeyFuncDict.set(value: _KT, _sa_initiator: AttributeEventToken | Literal[None, False] = None) None

Add an item by value, consulting the keyfunc for the key.

method sqlalchemy.orm.KeyFuncDict.setdefault(key, default=None)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

method sqlalchemy.orm.KeyFuncDict.update([E, ]**F) None.  Update D from dict/iterable E and F.

If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

sqlalchemy.orm.MappedCollection = <class 'sqlalchemy.orm.mapped_collection.KeyFuncDict'>

Base for ORM mapped dictionary classes.

Extends the dict type with additional methods needed by SQLAlchemy ORM collection classes. Use of KeyFuncDict is most directly by using the attribute_keyed_dict() or column_keyed_dict() class factories. KeyFuncDict may also serve as the base for user-defined custom dictionary classes.

Changed in version 2.0: Renamed MappedCollection to KeyFuncDict.

Collection Internals

Object Name Description

bulk_replace(values, existing_adapter, new_adapter[, initiator])

Load a new collection, firing events based on prior like membership.

collection

Decorators for entity collection classes.

collection_adapter

attrgetter(attr, …) –> attrgetter object

CollectionAdapter

Bridges between the ORM and arbitrary Python collections.

InstrumentedDict

An instrumented version of the built-in dict.

InstrumentedList

An instrumented version of the built-in list.

InstrumentedSet

An instrumented version of the built-in set.

prepare_instrumentation(factory)

Prepare a callable for future use as a collection class factory.

function sqlalchemy.orm.collections.bulk_replace(values, existing_adapter, new_adapter, initiator=None)

Load a new collection, firing events based on prior like membership.

Appends instances in values onto the new_adapter. Events will be fired for any instance not present in the existing_adapter. Any instances in existing_adapter not present in values will have remove events fired upon them.

Parameters:
class sqlalchemy.orm.collections.collection

Decorators for entity collection classes.

The decorators fall into two groups: annotations and interception recipes.

The annotating decorators (appender, remover, iterator, converter, internally_instrumented) indicate the method’s purpose and take no arguments. They are not written with parens:

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

The recipe decorators all require parens, even those that take no arguments:

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

@collection.removes_return()
def popitem(self): ...
method sqlalchemy.orm.collections.collection.static adds(arg)

Mark the method as adding an entity to the collection.

Adds “add to collection” handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value. Arguments can be specified positionally (i.e. integer) or by name:

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

@collection.adds('entity')
def do_stuff(self, thing, entity=None): ...
method sqlalchemy.orm.collections.collection.static appender(fn)

Tag the method as the collection appender.

The appender method is called with one positional argument: the value to append. The method will be automatically decorated with ‘adds(1)’ if not already decorated:

@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

If the value to append is not allowed in the collection, you may raise an exception. Something to remember is that the appender will be called for each object mapped by a database query. If the database contains rows that violate your collection semantics, you will need to get creative to fix the problem, as access via the collection will not work.

If the appender method is internally instrumented, you must also receive the keyword argument ‘_sa_initiator’ and ensure its promulgation to collection events.

method sqlalchemy.orm.collections.collection.static converter(fn)

Tag the method as the collection converter.

Deprecated since version 1.3: The collection.converter() handler is deprecated and will be removed in a future release. Please refer to the bulk_replace listener interface in conjunction with the listen() function.

This optional method will be called when a collection is being replaced entirely, as in:

myobj.acollection = [newvalue1, newvalue2]

The converter method will receive the object being assigned and should return an iterable of values suitable for use by the appender method. A converter must not assign values or mutate the collection, its sole job is to adapt the value the user provides into an iterable of values for the ORM’s use.

The default converter implementation will use duck-typing to do the conversion. A dict-like collection will be convert into an iterable of dictionary values, and other types will simply be iterated:

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

If the duck-typing of the object does not match the type of this collection, a TypeError is raised.

Supply an implementation of this method if you want to expand the range of possible types that can be assigned in bulk or perform validation on the values about to be assigned.

method sqlalchemy.orm.collections.collection.static internally_instrumented(fn)

Tag the method as instrumented.

This tag will prevent any decoration from being applied to the method. Use this if you are orchestrating your own calls to collection_adapter() in one of the basic SQLAlchemy interface methods, or to prevent an automatic ABC method decoration from wrapping your implementation:

# 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 sqlalchemy.orm.collections.collection.static iterator(fn)

Tag the method as the collection remover.

The iterator method is called with no arguments. It is expected to return an iterator over all collection members:

@collection.iterator
def __iter__(self): ...
method sqlalchemy.orm.collections.collection.static remover(fn)

Tag the method as the collection remover.

The remover method is called with one positional argument: the value to remove. The method will be automatically decorated with removes_return() if not already decorated:

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

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

If the value to remove is not present in the collection, you may raise an exception or return None to ignore the error.

If the remove method is internally instrumented, you must also receive the keyword argument ‘_sa_initiator’ and ensure its promulgation to collection events.

method sqlalchemy.orm.collections.collection.static removes(arg)

Mark the method as removing an entity in the collection.

Adds “remove from collection” handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value to be removed. Arguments can be specified positionally (i.e. integer) or by name:

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

For methods where the value to remove is not known at call-time, use collection.removes_return.

method sqlalchemy.orm.collections.collection.static removes_return()

Mark the method as removing an entity in the collection.

Adds “remove from collection” handling to the method. The return value of the method, if any, is considered the value to remove. The method arguments are not inspected:

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

For methods where the value to remove is known at call-time, use collection.remove.

method sqlalchemy.orm.collections.collection.static replaces(arg)

Mark the method as replacing an entity in the collection.

Adds “add to collection” and “remove from collection” handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value to be added, and return value, if any will be considered the value to remove.

Arguments can be specified positionally (i.e. integer) or by name:

@collection.replaces(2)
def __setitem__(self, index, item): ...
sqlalchemy.orm.collections.collection_adapter = operator.attrgetter('_sa_adapter')

attrgetter(attr, …) –> attrgetter object

Return a callable object that fetches the given attribute(s) from its operand. After f = attrgetter(‘name’), the call f(r) returns r.name. After g = attrgetter(‘name’, ‘date’), the call g(r) returns (r.name, r.date). After h = attrgetter(‘name.first’, ‘name.last’), the call h(r) returns (r.name.first, r.name.last).

class sqlalchemy.orm.collections.CollectionAdapter

Bridges between the ORM and arbitrary Python collections.

Proxies base-level collection operations (append, remove, iterate) to the underlying Python collection, and emits add/remove events for entities entering or leaving the collection.

The ORM uses CollectionAdapter exclusively for interaction with entity collections.

class sqlalchemy.orm.collections.InstrumentedDict

An instrumented version of the built-in dict.

Class signature

class sqlalchemy.orm.collections.InstrumentedDict (builtins.dict, typing.Generic)

class sqlalchemy.orm.collections.InstrumentedList

An instrumented version of the built-in list.

Class signature

class sqlalchemy.orm.collections.InstrumentedList (builtins.list, typing.Generic)

class sqlalchemy.orm.collections.InstrumentedSet

An instrumented version of the built-in set.

Class signature

class sqlalchemy.orm.collections.InstrumentedSet (builtins.set, typing.Generic)

function sqlalchemy.orm.collections.prepare_instrumentation(factory: Type[Collection[Any]] | Callable[[], _AdaptedCollectionProtocol]) Callable[[], _AdaptedCollectionProtocol]

Prepare a callable for future use as a collection class factory.

Given a collection class factory (either a type or no-arg callable), return another factory that will produce compatible instances when called.

This function is responsible for converting collection_class=list into the run-time behavior of collection_class=InstrumentedList.