Настройка коллекции и детали 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]) |
Тип коллекции на основе словаря с ключом на основе атрибутов. |
Тип коллекции на основе словаря с ключом на основе атрибутов. |
|
column_keyed_dict(mapping_spec, *, [ignore_unpopulated_attribute]) |
Тип коллекции на основе словаря с ключами на основе столбцов. |
Тип коллекции на основе словаря с ключами на основе столбцов. |
|
keyfunc_mapping(keyfunc, *, [ignore_unpopulated_attribute]) |
Тип коллекции на основе словаря с произвольным расположением ключей. |
База для классов словарей, сопоставленных с ORM. |
|
Тип коллекции на основе словаря с произвольным расположением ключей. |
|
База для классов словарей, сопоставленных с 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_spec –
Column
объект, который, как ожидается, будет отображен целевым отображателем на определенный атрибут отображаемого класса, значение которого на конкретном экземпляре будет использоваться в качестве ключа для новой словарной статьи для этого экземпляра.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_spec –
Column
объект, который, как ожидается, будет отображен целевым отображателем на определенный атрибут отображаемого класса, значение которого на конкретном экземпляре будет использоваться в качестве ключа для новой словарной статьи для этого экземпляра.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
.См.также
Members
__init__(), clear(), pop(), popitem(), remove(), set(), setdefault(), update()
Классная подпись
класс
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]
-
method
- 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]) |
Загружает новую коллекцию, вызывая события, основанные на предыдущем членстве. |
Декораторы для классов коллекции сущностей. |
|
attrgetter(attr, …) –> объект attrgetter |
|
Мосты между ORM и произвольными коллекциями Python. |
|
Инструментированная версия встроенного dict. |
|
Инструментированная версия встроенного списка. |
|
Инструментальная версия встроенного комплекта. |
|
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): ...
Все декораторы рецептов требуют паренсов, даже те, которые не принимают аргументов:
Members
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
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): ...
-
method
- 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 во время выполнения.