Настройка коллекции и детали API

Функция relationship() определяет связь между двумя классами. Если связь определяет отношения «один ко многим» или «многие ко многим», она представляется как коллекция Python при загрузке и манипулировании объектами. В этом разделе представлена дополнительная информация о конфигурации и методах работы с коллекциями.

Настройка доступа к коллекции

Сопоставление отношений один-ко-многим или многие-ко-многим приводит к созданию коллекции значений, доступных через атрибут родительского экземпляра. Двумя распространенными типами коллекций для них являются list и set, которые в отображениях Declarative, использующих 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"))

Или для set, проиллюстрированного в той же коллекции 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.List или typing.Set, например Mapped[List["Child"]] или Mapped[Set["Child"]]; встроенные модули Python list и set пока не поддерживают общие аннотации в этих версиях 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, например, при использовании imperative mappings или нетипизированного кода 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_class или Mapped, типом коллекции по умолчанию является list.

Помимо встроенных модулей list и set, имеется поддержка двух разновидностей словаря, описанных ниже в разделе Коллекции словарей. Существует также поддержка любого произвольного изменяемого типа последовательности, который может быть установлен в качестве целевой коллекции, с некоторыми дополнительными шагами настройки; это описано в разделе Реализация пользовательских коллекций.

Коллекции словарей

При использовании словаря в качестве коллекции требуется небольшая дополнительная деталь. Это связано с тем, что объекты всегда загружаются из базы данных в виде списков, и для правильного заполнения словаря необходимо иметь стратегию генерации ключей. Функция attribute_keyed_dict() является наиболее распространенным способом создания простой коллекции словарей. Она создает класс словаря, который будет применять определенный атрибут сопоставленного класса в качестве ключа. Ниже мы отображаем класс Item, содержащий словарь из Note элементов, ключом к которому служит атрибут Note.keyword. При использовании attribute_keyed_dict() аннотация Mapped может быть набрана с помощью 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() будет гарантировать, что атрибут .keyword каждого Note соответствует ключу в словаре. Например, при назначении на 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 передается непосредственно:

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» для создания упрощенных представлений словарей. Примеры смотрите в Проксирование к коллекциям на основе словарей и Доверенные лица объединений.

Работа с мутациями ключей и обратное наполнение для коллекций словарей

При использовании 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")

Выше, если мы создадим B(), который ссылается на определенный A(), то обратное заполнение добавит 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)

Реализация пользовательских коллекций

Вы также можете использовать собственные типы для коллекций. В простых случаях достаточно наследовать от list или set, добавив пользовательское поведение. В других случаях необходимы специальные декораторы, чтобы сообщить SQLAlchemy более подробную информацию о том, как работает коллекция.

Коллекции в SQLAlchemy прозрачно инструментируются. Инструментирование означает, что обычные операции над коллекцией отслеживаются и приводят к записи изменений в базу данных во время вспышки. Кроме того, операции над коллекцией могут вызывать события, которые указывают на необходимость выполнения некоторой вторичной операции. Примеры вторичных операций включают сохранение дочернего элемента в родительском Session (т.е. каскад save-update), а также синхронизацию состояния двунаправленных отношений (т.е. backref()).

Пакет collections понимает базовый интерфейс списков, множеств и dicts и автоматически применяет инструментарий к этим встроенным типам и их подклассам. Производные типы объектов, реализующие базовый интерфейс коллекций, обнаруживаются и инструментируются с помощью duck-typing:

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"

append, remove и extend являются известными членами list, и будут автоматически инструментированы. __iter__ не является методом-мутатором и не будет инструментироваться, и foo тоже не будет.

Утиная типизация (т.е. догадки), конечно, не является надежной, поэтому вы можете явно указать интерфейс, который вы реализуете, предоставив атрибут класса __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)

Этот класс похож на Python list (т.е. «спископодобный»), поскольку имеет метод append, но атрибут __emulates__ заставляет рассматривать его как set. Известно, что remove является частью интерфейса set и будет инструментироваться.

Но этот класс еще не работает: необходимо немного подправить его для использования в SQLAlchemy. ORM должен знать, какие методы использовать для добавления, удаления и итерации членов коллекции. При использовании таких типов, как list или set, соответствующие методы хорошо известны и используются автоматически при их наличии. Однако приведенный выше класс, лишь приблизительно напоминающий set, не предоставляет ожидаемого метода add, поэтому мы должны указать ORM метод, который будет вместо него использовать метод add, в данном случае с помощью декоратора @collection.appender; это будет показано в следующем разделе.

Аннотирование пользовательских коллекций с помощью декораторов

Декораторы можно использовать для обозначения отдельных методов, необходимых 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):
        ...

Нет требования, чтобы коллекция была «спископодобной» или «набортной». Классы коллекций могут быть любой формы, лишь бы они имели интерфейс append, remove и iterate, обозначенный для использования SQLAlchemy. Методы append и remove будут вызываться с сопоставленной сущностью в качестве единственного аргумента, а методы iterator вызываются без аргументов и должны возвращать итератор.

Пользовательские коллекции на основе словарей

Класс 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 или обеспечить диктоподобное поведение коллекции в классе с типом «утка». Однако вы должны украсить методы appender и remover - в базовом интерфейсе словаря нет совместимых методов, которые SQLAlchemy использовал бы по умолчанию. Итерация будет проходить через values(), если не оформлено иначе.

Инструментарий и пользовательские типы

Многие пользовательские типы и существующие библиотечные классы можно использовать в качестве типа коллекции сущностей как есть, без лишних слов. Однако важно отметить, что процесс инструментации изменит тип, автоматически добавляя декораторы вокруг методов.

Декоративные элементы легки и не требуют вмешательства вне отношений, но они добавляют ненужные накладные расходы, когда срабатывают в других местах. При использовании библиотечного класса в качестве коллекции, хорошей практикой может быть использование трюка «тривиальный подкласс», чтобы ограничить декорации только вашим использованием в отношениях. Например:

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


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

ORM использует этот подход для встроенных элементов, спокойно подставляя тривиальный подкласс, когда list, set или dict используется напрямую.

API коллекционирования

Object Name Description

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[_KT, _KT]]

Тип коллекции на основе словаря с ключом на основе атрибутов.

Изменено в версии 2.0: Переименовал attribute_mapped_collection в attribute_keyed_dict().

Возвращает фабрику KeyFuncDict, которая будет производить новые ключи словаря на основе значения определенного именованного атрибута на экземплярах ORM mapped, которые будут добавлены в словарь.

Примечание

значение целевого атрибута должно быть присвоено его значению в момент добавления объекта в коллекцию словаря. Кроме того, изменения атрибута key не отслеживаются, что означает, что ключ в словаре не синхронизируется автоматически со значением ключа на самом целевом объекте. Более подробную информацию см. в разделе Работа с мутациями ключей и обратное наполнение для коллекций словарей.

См.также

Коллекции словарей - история использования

Параметры:
  • attr_name – строковое имя ORM-атрибута отображаемого класса, значение которого для конкретного экземпляра будет использоваться как ключ для новой словарной статьи для этого экземпляра.

  • ignore_unpopulated_attribute – если True, и атрибут target на объекте вообще не заполнен, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 2.0 По умолчанию выдается ошибка, если установлено, что атрибут, используемый для ключа словаря, никогда не был заполнен каким-либо значением. Можно установить параметр attribute_keyed_dict.ignore_unpopulated_attribute, который вместо этого укажет, что это условие должно быть проигнорировано, а операция добавления тихо пропущена. Это отличается от поведения серии 1.x, которая ошибочно заполняла значение в словаре с произвольным значением ключа None.

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

Тип коллекции на основе словаря с ключами на основе столбцов.

Изменено в версии 2.0: Переименовал column_mapped_collection в column_keyed_dict.

Возвращает фабрику KeyFuncDict, которая будет производить новые ключи словаря на основе значения определенного Column сопоставленного атрибута на сопоставленных экземплярах ORM, которые будут добавлены в словарь.

Примечание

значение целевого атрибута должно быть присвоено его значению в момент добавления объекта в коллекцию словаря. Кроме того, изменения атрибута key не отслеживаются, что означает, что ключ в словаре не синхронизируется автоматически со значением ключа на самом целевом объекте. Более подробную информацию см. в разделе Работа с мутациями ключей и обратное наполнение для коллекций словарей.

См.также

Коллекции словарей - история использования

Параметры:
  • mapping_specColumn объект, который, как ожидается, будет отображен целевым отображателем на определенный атрибут отображаемого класса, значение которого на конкретном экземпляре будет использоваться в качестве ключа для новой словарной статьи для этого экземпляра.

  • ignore_unpopulated_attribute – если True, и сопоставленный атрибут, указанный данным целевым атрибутом Column на объекте вообще не заполнен, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 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().

Возвращает фабрику KeyFuncDict с функцией ключа, сгенерированной из keyfunc - вызываемой функции, которая принимает сущность и возвращает значение ключа.

Примечание

данная функция keyfunc вызывается только один раз в момент добавления целевого объекта в коллекцию. Изменения эффективного значения, возвращаемого функцией, не отслеживаются.

См.также

Коллекции словарей - история использования

Параметры:
  • keyfunc – callable, которому будет передан экземпляр ORM-mapped, который затем должен сгенерировать новый ключ для использования в словаре. Если возвращаемое значение равно LoaderCallableStatus.NO_VALUE, будет выдана ошибка.

  • ignore_unpopulated_attribute – если True, и вызываемая программа возвращает LoaderCallableStatus.NO_VALUE для конкретного экземпляра, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 2.0 по умолчанию выдается ошибка, если callable, используемая для ключа словаря, возвращает 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 mapped, которые будут добавлены в словарь.

Примечание

значение целевого атрибута должно быть присвоено его значению в момент добавления объекта в коллекцию словаря. Кроме того, изменения атрибута key не отслеживаются, что означает, что ключ в словаре не синхронизируется автоматически со значением ключа на самом целевом объекте. Более подробную информацию см. в разделе Работа с мутациями ключей и обратное наполнение для коллекций словарей.

См.также

Коллекции словарей - история использования

Параметры:
  • attr_name – строковое имя ORM-атрибута отображаемого класса, значение которого для конкретного экземпляра будет использоваться как ключ для новой словарной статьи для этого экземпляра.

  • ignore_unpopulated_attribute – если True, и атрибут target на объекте вообще не заполнен, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 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, которая будет производить новые ключи словаря на основе значения определенного Column сопоставленного атрибута на сопоставленных экземплярах ORM, которые будут добавлены в словарь.

Примечание

значение целевого атрибута должно быть присвоено его значению в момент добавления объекта в коллекцию словаря. Кроме того, изменения атрибута key не отслеживаются, что означает, что ключ в словаре не синхронизируется автоматически со значением ключа на самом целевом объекте. Более подробную информацию см. в разделе Работа с мутациями ключей и обратное наполнение для коллекций словарей.

См.также

Коллекции словарей - история использования

Параметры:
  • mapping_specColumn объект, который, как ожидается, будет отображен целевым отображателем на определенный атрибут отображаемого класса, значение которого на конкретном экземпляре будет использоваться в качестве ключа для новой словарной статьи для этого экземпляра.

  • ignore_unpopulated_attribute – если True, и сопоставленный атрибут, указанный данным целевым атрибутом Column на объекте вообще не заполнен, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 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 – callable, которому будет передан экземпляр ORM-mapped, который затем должен сгенерировать новый ключ для использования в словаре. Если возвращаемое значение равно LoaderCallableStatus.NO_VALUE, будет выдана ошибка.

  • ignore_unpopulated_attribute – если True, и вызываемая программа возвращает LoaderCallableStatus.NO_VALUE для конкретного экземпляра, операция будет молча пропущена. По умолчанию выдается ошибка. … versionadded:: 2.0 по умолчанию выдается ошибка, если callable, используемая для ключа словаря, возвращает LoaderCallableStatus.NO_VALUE, что в контексте атрибута ORM означает атрибут, который никогда не был заполнен никаким значением. Можно установить параметр mapped_collection.ignore_unpopulated_attribute, который вместо этого укажет, что это условие должно быть проигнорировано, а операция добавления тихо пропущена. Это отличается от поведения серии 1.x, которая ошибочно заполняла значение в словаре с произвольным значением ключа None.

class sqlalchemy.orm.KeyFuncDict

База для классов словарей, сопоставленных с ORM.

Расширяет тип dict дополнительными методами, необходимыми классам коллекций SQLAlchemy ORM. Использование KeyFuncDict наиболее прямое - с помощью фабрик классов attribute_keyed_dict() или column_keyed_dict(). KeyFuncDict также может служить основой для пользовательских классов пользовательских словарей.

Изменено в версии 2.0: Переименовал MappedCollection в KeyFuncDict.

Классная подпись

класс sqlalchemy.orm.KeyFuncDict (builtins.dict, typing.Generic)

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

Создайте новую коллекцию с ключом, предоставленным функцией keyfunc.

keyfunc может быть любой вызываемой функцией, которая принимает объект и возвращает объект для использования в качестве ключа словаря.

Функция keyfunc будет вызываться каждый раз, когда ORM нужно добавить член только по значению (например, при загрузке экземпляров из базы данных) или удалить член. Применимы обычные предостережения относительно создания ключей по словарю - keyfunc(object) должен возвращать один и тот же результат на протяжении всей жизни коллекции. Использование ключей на основе изменяемых свойств может привести к тому, что недоступные экземпляры «потеряются» в коллекции.

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.

Если ключ не найден, возвращает значение по умолчанию, если оно задано; в противном случае выдает ошибку KeyError.

method sqlalchemy.orm.KeyFuncDict.popitem()

Удалить и вернуть пару (ключ, значение) в виде кортежа.

Пары возвращаются в порядке LIFO (последним пришел, первым ушел). Вызывает KeyError, если диктант пуст.

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

Удалить элемент по значению, обращаясь к keyfunc для ключа.

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

Добавление элемента по значению, консультируясь с keyfunc для ключа.

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

Вставка ключа со значением по умолчанию, если ключ отсутствует в словаре.

Возвращает значение для ключа, если ключ находится в словаре, иначе по умолчанию.

method sqlalchemy.orm.KeyFuncDict.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. Использование KeyFuncDict наиболее прямое - с помощью фабрик классов attribute_keyed_dict() или column_keyed_dict(). KeyFuncDict также может служить основой для пользовательских классов пользовательских словарей.

Изменено в версии 2.0: Переименовал MappedCollection в KeyFuncDict.

Внутренние компоненты коллекции

Object Name Description

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

Загружает новую коллекцию, вызывая события, основанные на предыдущем членстве.

collection

Декораторы для классов коллекции сущностей.

collection_adapter

attrgetter(attr, …) –> объект attrgetter

CollectionAdapter

Мосты между ORM и произвольными коллекциями Python.

InstrumentedDict

Инструментированная версия встроенного dict.

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 – A CollectionAdapter экземпляров, подлежащих замене

  • new_adapter – Пустой CollectionAdapter для загрузки с values

class sqlalchemy.orm.collections.collection

Декораторы для классов коллекции сущностей.

Декораторы делятся на две группы: аннотации и рецепты перехвата.

Аннотирующие декораторы (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 adds(arg)

Пометить метод как добавляющий сущность в коллекцию.

Добавляет в метод обработку «добавить в коллекцию». Аргумент декоратора указывает, какой аргумент метода содержит значение, относящееся к SQLAlchemy. Аргументы могут быть указаны позиционно (т.е. целочисленно) или по имени:

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

Пометьте метод как приложение коллекции.

Метод 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 является внутренне инструментированным, вы также должны получить аргумент ключевого слова „_sa_initiator“ и обеспечить его распространение на события коллекции.

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

Пометьте метод как преобразователь коллекции.

Не рекомендуется, начиная с версии 1.3: Обработчик collection.converter() устарел и будет удален в одном из будущих выпусков. Обратитесь к интерфейсу слушателя bulk_replace в сочетании с функцией listen().

Этот необязательный метод будет вызван, когда коллекция заменяется полностью, как в:

myobj.acollection = [newvalue1, newvalue2]

Метод конвертера получает присваиваемый объект и должен вернуть итерабельную коллекцию значений, пригодную для использования методом appender. Конвертер не должен присваивать значения или изменять коллекцию, его единственная задача - адаптировать значение, предоставленное пользователем, в итерабельную таблицу значений для использования ORM.

Реализация конвертера по умолчанию будет использовать утиную типизацию для преобразования. Коллекция типа dict будет преобразована в итерабельную коллекцию значений словаря, а другие типы будут просто итерированы:

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

Если duck-типизация объекта не соответствует типу этой коллекции, возникает ошибка TypeError.

Предоставьте реализацию этого метода, если вы хотите расширить диапазон возможных типов, которые могут быть присвоены массово, или выполнить валидацию значений, которые будут присвоены.

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

Пометьте метод как средство удаления коллекции.

Метод iterator вызывается без аргументов. Ожидается, что он вернет итератор по всем членам коллекции:

@collection.iterator
def __iter__(self): ...
method sqlalchemy.orm.collections.collection.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 sqlalchemy.orm.collections.collection.static removes(arg)

Пометить метод как удаляющий объект в коллекции.

Добавляет в метод обработку «удалить из коллекции». Аргумент декоратора указывает, какой аргумент метода содержит значение, относящееся к 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 replaces(arg)

Пометить метод как заменяющий объект в коллекции.

Добавляет в метод обработку «добавить в коллекцию» и «удалить из коллекции». Аргумент decorator указывает, какой аргумент метода содержит значение, относящееся к 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.

Проксирует операции коллекции базового уровня (append, remove, iterate) на базовую коллекцию Python, и издает события add/remove для объектов, входящих в коллекцию или покидающих ее.

ORM использует CollectionAdapter исключительно для взаимодействия с коллекциями сущностей.

class sqlalchemy.orm.collections.InstrumentedDict

Инструментированная версия встроенного dict.

Классная подпись

класс 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: Union[Type[Collection[Any]], Callable[[], _AdaptedCollectionProtocol]]) Callable[[], _AdaptedCollectionProtocol]

Подготовьте вызываемый объект для дальнейшего использования в качестве фабрики классов коллекций.

Учитывая фабрику класса коллекции (тип или no-arg callable), верните другую фабрику, которая при вызове будет производить совместимые экземпляры.

Эта функция отвечает за преобразование collection_class=list в поведение collection_class=InstrumentedList во время выполнения.

Вернуться на верх