Составление сопоставленных иерархий с помощью миксинов¶
При отображении классов с использованием стиля Declarative часто возникает необходимость разделить общую функциональность, такую как определенные колонки, параметры таблицы или отображающего устройства, схемы именования или другие отображаемые свойства, между многими классами. При использовании декларативных отображений эта идиома поддерживается с помощью mixin classes, а также путем дополнения самого декларативного базового класса.
Совет
В дополнение к классам-миксинам, общие параметры столбцов могут также совместно использоваться многими классами с помощью типов PEP 593 Annotated
; смотрите Сопоставление нескольких конфигураций типов с типами Python и Сопоставление объявлений целых столбцов с типами Python для получения информации об этих возможностях SQLAlchemy 2.0.
Ниже приведен пример некоторых часто смешиваемых идиом:
from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
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 CommonMixin:
"""define a series of common elements that may be applied to mapped
classes using this class as a mixin class."""
@declared_attr.directive
def __tablename__(cls) -> str:
return cls.__name__.lower()
__table_args__ = {"mysql_engine": "InnoDB"}
__mapper_args__ = {"eager_defaults": True}
id: Mapped[int] = mapped_column(primary_key=True)
class HasLogRecord:
"""mark classes that have a many-to-one relationship to the
``LogRecord`` class."""
log_record_id: Mapped[int] = mapped_column(ForeignKey("logrecord.id"))
@declared_attr
def log_record(self) -> Mapped["LogRecord"]:
return relationship("LogRecord")
class LogRecord(CommonMixin, Base):
log_info: Mapped[str]
class MyModel(CommonMixin, HasLogRecord, Base):
name: Mapped[str]
Приведенный выше пример иллюстрирует класс MyModel
, который включает в свою базу два миксина CommonMixin
и HasLogRecord
, а также дополнительный класс LogRecord
, который также включает CommonMixin
, демонстрируя разнообразие конструкций, поддерживаемых миксинами и базовыми классами, включая:
колонки, объявленные с помощью
mapped_column()
,Mapped
илиColumn
, копируются из миксинов или базовых классов в целевой класс для отображения; выше это показано с помощью атрибутов колонокCommonMixin.id
иHasLogRecord.log_record_id
.Декларативные директивы, такие как
__table_args__
и__mapper_args__
, могут быть присвоены миксину или базовому классу, где они будут автоматически действовать для всех классов, которые наследуются от миксина или базы. Приведенный выше пример иллюстрирует это с помощью атрибутов__table_args__
и__mapper_args__
.Все директивы Declarative, включая все директивы
__tablename__
,__table__
,__table_args__
и__mapper_args__
, могут быть реализованы с помощью определяемых пользователем методов класса, которые украшаются декораторомdeclared_attr
(в частности, подчленомdeclared_attr.directive
, подробнее об этом чуть позже). Выше это показано на примере метода классаdef __tablename__(cls)
, который динамически генерирует имяTable
; при применении к классуMyModel
имя таблицы будет сгенерировано как"mymodel"
, а при применении к классуLogRecord
имя таблицы будет сгенерировано как"logrecord"
.Другие свойства ORM, такие как
relationship()
, могут быть сгенерированы на целевом классе для отображения с помощью пользовательских методов класса, также украшенных декораторомdeclared_attr
. Выше это было проиллюстрировано на примере генерации свойства многие-к-одномуrelationship()
для сопоставленного объекта под названиемLogRecord
.
Все описанные выше возможности можно продемонстрировать на примере select()
:
>>> from sqlalchemy import select
>>> print(select(MyModel).join(MyModel.log_record))
{printsql}SELECT mymodel.name, mymodel.id, mymodel.log_record_id
FROM mymodel JOIN logrecord ON logrecord.id = mymodel.log_record_id
Совет
В примерах declared_attr
будет сделана попытка проиллюстрировать правильные аннотации PEP 484 для каждого примера метода. Использование аннотаций в функциях declared_attr
является совершенно необязательным и не используется в Declarative; однако эти аннотации необходимы для прохождения проверки типов Mypy --strict
.
Кроме того, подчлен declared_attr.directive
, проиллюстрированный выше, также является необязательным и имеет значение только для инструментов типизации PEP 484, поскольку он корректирует ожидаемый тип возврата при создании методов для переопределения декларативных директив, таких как __tablename__
, __mapper_args__
и __table_args__
.
Добавлено в версии 2.0: В рамках поддержки типизации PEP 484 для SQLAlchemy ORM, добавили declared_attr.directive
к declared_attr
для различения между Mapped
атрибутами и декларативными конфигурационными атрибутами
Не существует фиксированного соглашения о порядке следования миксинов и базовых классов. Применяются обычные правила разрешения методов Python, и приведенный выше пример будет работать так же хорошо с:
class MyModel(Base, HasLogRecord, CommonMixin):
name: Mapped[str] = mapped_column()
Это работает потому, что Base
здесь не определяет ни одну из переменных, которые определяют CommonMixin
или HasLogRecord
, т.е. __tablename__
, __table_args__
, id
и т.д. Если бы Base
определял одноименный атрибут, то класс, стоящий первым в списке наследований, определял бы, какой атрибут используется во вновь определенном классе.
Совет
Хотя в приведенном выше примере используется форма Annotated Declarative Table, основанная на аннотационном классе Mapped
, классы mixin также прекрасно работают с неаннотированными и унаследованными декларативными формами, например, при использовании Column
непосредственно вместо mapped_column()
.
Изменено в версии 2.0: Для пользователей SQLAlchemy версии 1.4, которые, возможно, использовали mypy plugin, декоратор класса declarative_mixin()
больше не нужен для маркировки декларативных миксинов, предполагая, что плагин mypy больше не используется.
Расширение базы¶
Помимо использования чистого миксина, большинство приемов, описанных в этом разделе, можно также применять непосредственно к базовому классу для получения шаблонов, которые должны применяться ко всем классам, производным от конкретной базы. Пример ниже иллюстрирует некоторые из примеров предыдущего раздела в терминах класса Base
:
from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
"""define a series of common elements that may be applied to mapped
classes using this class as a base class."""
@declared_attr.directive
def __tablename__(cls) -> str:
return cls.__name__.lower()
__table_args__ = {"mysql_engine": "InnoDB"}
__mapper_args__ = {"eager_defaults": True}
id: Mapped[int] = mapped_column(primary_key=True)
class HasLogRecord:
"""mark classes that have a many-to-one relationship to the
``LogRecord`` class."""
log_record_id: Mapped[int] = mapped_column(ForeignKey("logrecord.id"))
@declared_attr
def log_record(self) -> Mapped["LogRecord"]:
return relationship("LogRecord")
class LogRecord(Base):
log_info: Mapped[str]
class MyModel(HasLogRecord, Base):
name: Mapped[str]
Где выше, MyModel
и LogRecord
, производные от Base
, будут иметь имя таблицы, производное от имени класса, столбец первичного ключа с именем id
, а также вышеуказанные аргументы таблицы и маппера, определенные Base.__table_args__
и Base.__mapper_args__
.
При использовании унаследованных declarative_base()
или registry.generate_base()
, параметр declarative_base.cls
может быть использован следующим образом для создания эквивалентного эффекта, как показано в неаннотированном примере ниже:
# legacy declarative_base() use
from sqlalchemy import Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base:
"""define a series of common elements that may be applied to mapped
classes using this class as a base class."""
@declared_attr.directive
def __tablename__(cls):
return cls.__name__.lower()
__table_args__ = {"mysql_engine": "InnoDB"}
__mapper_args__ = {"eager_defaults": True}
id = mapped_column(Integer, primary_key=True)
Base = declarative_base(cls=Base)
class HasLogRecord:
"""mark classes that have a many-to-one relationship to the
``LogRecord`` class."""
log_record_id = mapped_column(ForeignKey("logrecord.id"))
@declared_attr
def log_record(self):
return relationship("LogRecord")
class LogRecord(Base):
log_info = mapped_column(String)
class MyModel(HasLogRecord, Base):
name = mapped_column(String)
Смешивание в колоннах¶
Колонки могут быть указаны в миксинах при условии использования стиля конфигурации Declarative table (в отличие от конфигурации imperative table), так что колонки, объявленные в миксине, могут быть затем скопированы, чтобы стать частью Table
, который генерирует процесс Declarative. Все три конструкции mapped_column()
, Mapped
и Column
могут быть объявлены inline в декларативном миксине:
class TimestampMixin:
created_at: Mapped[datetime] = mapped_column(default=func.now())
updated_at: Mapped[datetime]
class MyModel(TimestampMixin, Base):
__tablename__ = "test"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
Там, где указано выше, все декларативные классы, включающие TimestampMixin
в свои базы классов, автоматически включают колонку created_at
, которая применяет метку времени ко всем вставкам строк, а также колонку updated_at
, которая не включает значение по умолчанию для целей примера (если бы она включала, мы бы использовали параметр Column.onupdate
, который принимается mapped_column()
). Эти конструкции столбцов всегда копируются из исходного миксина или базового класса, так что один и тот же миксин/базовый класс может быть применен к любому количеству целевых классов, каждый из которых будет иметь свои собственные конструкции столбцов.
Все декларативные формы колонок поддерживаются миксинами, включая:
Аннотированные атрибуты - с присутствием или без присутствия
mapped_column()
:class TimestampMixin: created_at: Mapped[datetime] = mapped_column(default=func.now()) updated_at: Mapped[datetime]
mapped_column - с или без
Mapped
присутствует:class TimestampMixin: created_at = mapped_column(default=func.now()) updated_at: Mapped[datetime] = mapped_column()
Колонна - наследие Декларативная форма:
class TimestampMixin: created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime)
В каждой из приведенных выше форм Declarative обрабатывает атрибуты на основе столбцов в классе mixin путем создания копии конструкции, которая затем применяется к целевому классу.
Изменено в версии 2.0: Декларативный API теперь может принимать объекты Column
, а также конструкции mapped_column()
любой формы при использовании миксинов без необходимости использования declared_attr()
. Предыдущие ограничения, которые не позволяли использовать колонки с элементами ForeignKey
непосредственно в миксинах, были устранены.
Смешение в отношениях¶
Отношения, созданные с помощью relationship()
, снабжаются декларативными классами-миксинами исключительно с использованием подхода declared_attr
, что устраняет любую двусмысленность, которая может возникнуть при копировании отношения и его, возможно, связанного с колонками содержимого. Ниже приведен пример, в котором столбец внешнего ключа и отношение сочетаются таким образом, что два класса Foo
и Bar
могут быть настроены так, чтобы ссылаться на общий целевой класс по принципу «многие-к-одному:»:
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class RefTargetMixin:
target_id: Mapped[int] = mapped_column(ForeignKey("target.id"))
@declared_attr
def target(cls) -> Mapped["Target"]:
return relationship("Target")
class Foo(RefTargetMixin, Base):
__tablename__ = "foo"
id: Mapped[int] = mapped_column(primary_key=True)
class Bar(RefTargetMixin, Base):
__tablename__ = "bar"
id: Mapped[int] = mapped_column(primary_key=True)
class Target(Base):
__tablename__ = "target"
id: Mapped[int] = mapped_column(primary_key=True)
С помощью приведенного выше отображения каждый из Foo
и Bar
содержит отношение к Target
, доступ к которому осуществляется через атрибут .target
:
>>> from sqlalchemy import select
>>> print(select(Foo).join(Foo.target))
{printsql}SELECT foo.id, foo.target_id
FROM foo JOIN target ON target.id = foo.target_id{stop}
>>> print(select(Bar).join(Bar.target))
{printsql}SELECT bar.id, bar.target_id
FROM bar JOIN target ON target.id = bar.target_id{stop}
Специальные аргументы, такие как relationship.primaryjoin
, могут также использоваться в смешанных методах класса, которые часто должны ссылаться на сопоставляемый класс. Для схем, которым необходимо ссылаться на локально отображаемые столбцы, в обычных случаях эти столбцы предоставляются Declarative как атрибуты отображаемого класса, который передается в качестве аргумента cls
в декорированный метод класса. Используя эту возможность, мы можем, например, переписать метод RefTargetMixin.target
с помощью явного primaryjoin, который ссылается на ожидающие отображения столбцы в Target
и cls
:
class Target(Base):
__tablename__ = "target"
id: Mapped[int] = mapped_column(primary_key=True)
class RefTargetMixin:
target_id: Mapped[int] = mapped_column(ForeignKey("target.id"))
@declared_attr
def target(cls) -> Mapped["Target"]:
# illustrates explicit 'primaryjoin' argument
return relationship("Target", primaryjoin=Target.id == cls.target_id)
Смешивание column_property()
и других MapperProperty
классов¶
Как и relationship()
, другие подклассы MapperProperty
, такие как column_property()
, также нуждаются в создании локальных для класса копий, когда используются миксинами, поэтому они также объявляются внутри функций, оформленных declared_attr
. Внутри функции другие обычные отображаемые колонки, которые были объявлены с помощью mapped_column()
, Mapped
или Column
, будут доступны из аргумента cls
, чтобы их можно было использовать для составления новых атрибутов, как в примере ниже, который складывает две колонки вместе:
from sqlalchemy.orm import column_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class SomethingMixin:
x: Mapped[int]
y: Mapped[int]
@declared_attr
def x_plus_y(cls) -> Mapped[int]:
return column_property(cls.x + cls.y)
class Something(SomethingMixin, Base):
__tablename__ = "something"
id: Mapped[int] = mapped_column(primary_key=True)
Выше мы можем использовать Something.x_plus_y
в операторе, где он выдает полное выражение:
>>> from sqlalchemy import select
>>> print(select(Something.x_plus_y))
{printsql}SELECT something.x + something.y AS anon_1
FROM something
Совет
Декоратор declared_attr
заставляет декорированную вызываемую функцию вести себя точно так же, как классметод. Однако инструменты типизации, такие как Pylance, могут не распознать это, что иногда может привести к тому, что они будут жаловаться на доступ к переменной cls
внутри тела функции. Чтобы решить эту проблему, декоратор @classmethod
может быть объединен непосредственно с declared_attr
как:
class SomethingMixin:
x: Mapped[int]
y: Mapped[int]
@declared_attr
@classmethod
def x_plus_y(cls) -> Mapped[int]:
return column_property(cls.x + cls.y)
Добавлено в версии 2.0: - declared_attr
can accommodate a
function decorated with @classmethod
to help with PEP 484
integration where needed.
Использование миксинов и базовых классов с шаблонами сопоставленного наследования¶
При работе с шаблонами наследования mapper, документированными в Отображение иерархий наследования классов, некоторые дополнительные возможности появляются при использовании declared_attr
либо с классами-миксинами, либо при дополнении как сопоставленных, так и несопоставленных суперклассов в иерархии классов.
При определении функций, украшенных declared_attr
на миксинах или базовых классах для интерпретации подклассами в иерархии наследования с отображением, необходимо проводить важное различие между функциями, которые генерируют специальные имена, используемые Declarative, такие как __tablename__
, __mapper_args__
, и теми, которые могут генерировать обычные отображаемые атрибуты, такие как mapped_column()
и relationship()
. Функции, определяющие директивы Declarative, вызываются для каждого подкласса в иерархии, тогда как функции, генерирующие сопоставленные атрибуты, вызываются только для первого сопоставленного суперкласса в иерархии.
Обоснование такого различия в поведении основано на том, что сопоставленные свойства уже наследуются классами, например, определенный столбец в сопоставленной таблице суперкласса не должен дублироваться и в подклассе, тогда как элементы, специфичные для конкретного класса или его сопоставленной таблицы, не наследуются, например, имя локально сопоставленной таблицы.
Разница в поведении между этими двумя вариантами использования демонстрируется в следующих двух разделах.
Использование declared_attr()
с наследуемыми аргументами Table
и Mapper
¶
Обычный рецепт использования микшинов заключается в создании функции def __tablename__(cls)
, которая динамически генерирует имя для сопоставленного Table
.
Этот рецепт можно использовать для генерации имен таблиц для наследуемой иерархии маппера, как в примере ниже, который создает миксин, дающий каждому классу простое имя таблицы на основе имени класса. Рецепт показан ниже, где имя таблицы генерируется для сопоставленного класса Person
и подкласса Engineer
от Person
, но не для подкласса Manager
от Person
:
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class Tablename:
@declared_attr.directive
def __tablename__(cls) -> Optional[str]:
return cls.__name__.lower()
class Person(Tablename, Base):
id: Mapped[int] = mapped_column(primary_key=True)
discriminator: Mapped[str]
__mapper_args__ = {"polymorphic_on": "discriminator"}
class Engineer(Person):
id: Mapped[int] = mapped_column(ForeignKey("person.id"), primary_key=True)
primary_language: Mapped[str]
__mapper_args__ = {"polymorphic_identity": "engineer"}
class Manager(Person):
@declared_attr.directive
def __tablename__(cls) -> Optional[str]:
"""override __tablename__ so that Manager is single-inheritance to Person"""
return None
__mapper_args__ = {"polymorphic_identity": "manager"}
В приведенном выше примере, как базовый класс Person
, так и класс Engineer
, являющиеся подклассами класса Tablename
mixin, который генерирует новые имена таблиц, будут иметь атрибут generated __tablename__
, что в Declarative указывает на то, что каждый класс должен иметь свой собственный генерируемый Table
, с которым он будет сопоставлен. Для подкласса Engineer
применяется стиль наследования joined table inheritance, так как он будет отображен на таблицу engineer
, которая присоединяется к базовой таблице person
. Любые другие подклассы, наследующие от Person
, также будут иметь этот стиль наследования по умолчанию (и в данном конкретном примере для каждого из них потребуется указать столбец первичного ключа; подробнее об этом в следующем разделе).
Напротив, подкласс Manager
класса Person
переопределяет метод класса __tablename__
, чтобы вернуть None
. Это указывает Declarative, что данный класс **не должен иметь порожденного Table
, и вместо этого будет использовать исключительно базовый Table
, к которому привязан Person
. Для подкласса Manager
применяется стиль наследования single table inheritance.
Приведенный пример показывает, что декларативные директивы типа __tablename__
обязательно применяются к каждому подклассу индивидуально, поскольку каждый сопоставленный класс должен указать, к какому Table
он будет сопоставлен, или будет ли он сам сопоставлен с Table
наследующего суперкласса.
Если мы хотим обратно изменить схему таблиц по умолчанию, показанную выше, чтобы наследование одной таблицы было по умолчанию, а наследование объединенных таблиц могло быть определено только при наличии директивы __tablename__
для ее переопределения, мы можем использовать декларативные помощники в самом верхнем методе __tablename__()
, в данном случае помощник под названием has_inherited_table()
. Эта функция вернет True
, если суперкласс уже сопоставлен с Table
. Мы можем использовать этот помощник внутри самого базового метода класса __tablename__()
, чтобы условно возвращать None
для имени таблицы, если таблица уже существует, таким образом указывая на однотабличное наследование для наследуемых подклассов по умолчанию:
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import has_inherited_table
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class Tablename:
@declared_attr.directive
def __tablename__(cls):
if has_inherited_table(cls):
return None
return cls.__name__.lower()
class Person(Tablename, Base):
id: Mapped[int] = mapped_column(primary_key=True)
discriminator: Mapped[str]
__mapper_args__ = {"polymorphic_on": "discriminator"}
class Engineer(Person):
@declared_attr.directive
def __tablename__(cls):
"""override __tablename__ so that Engineer is joined-inheritance to Person"""
return cls.__name__.lower()
id: Mapped[int] = mapped_column(ForeignKey("person.id"), primary_key=True)
primary_language: Mapped[str]
__mapper_args__ = {"polymorphic_identity": "engineer"}
class Manager(Person):
__mapper_args__ = {"polymorphic_identity": "manager"}
Использование declared_attr()
для создания столбцов наследования для конкретной таблицы¶
В отличие от того, как обрабатываются __tablename__
и другие специальные имена при использовании declared_attr
, когда мы смешиваем столбцы и свойства (например, отношения, свойства столбцов и т.д.), функция вызывается только для базового класса в иерархии, если только директива declared_attr
не используется в сочетании с поддирективой declared_attr.cascading
. Ниже, только класс Person
получит столбец с именем id
; отображение будет неудачным для Engineer
, которому не присвоен первичный ключ:
class HasId:
id: Mapped[int] = mapped_column(primary_key=True)
class Person(HasId, Base):
__tablename__ = "person"
discriminator: Mapped[str]
__mapper_args__ = {"polymorphic_on": "discriminator"}
# this mapping will fail, as there's no primary key
class Engineer(Person):
__tablename__ = "engineer"
primary_language: Mapped[str]
__mapper_args__ = {"polymorphic_identity": "engineer"}
Обычно при наследовании объединенных таблиц мы хотим иметь колонки с разными именами в каждом подклассе. Однако в данном случае мы можем захотеть иметь столбец id
в каждой таблице, и чтобы они ссылались друг на друга через внешний ключ. Мы можем добиться этого с помощью модификатора declared_attr.cascading
, который указывает, что функция должна быть вызвана для каждого класса в иерархии, почти (см. предупреждение ниже) таким же образом, как и для __tablename__
:
class HasIdMixin:
@declared_attr.cascading
def id(cls) -> Mapped[int]:
if has_inherited_table(cls):
return mapped_column(ForeignKey("person.id"), primary_key=True)
else:
return mapped_column(Integer, primary_key=True)
class Person(HasIdMixin, Base):
__tablename__ = "person"
discriminator: Mapped[str]
__mapper_args__ = {"polymorphic_on": "discriminator"}
class Engineer(Person):
__tablename__ = "engineer"
primary_language: Mapped[str]
__mapper_args__ = {"polymorphic_identity": "engineer"}
Предупреждение
Функция declared_attr.cascading
в настоящее время не позволяет подклассу переопределить атрибут с другой функцией или значением. Это текущее ограничение в механике разрешения @declared_attr
, и при обнаружении этого условия выдается предупреждение. Это ограничение относится только к колонкам, отображаемым ORM, отношениям и другим стилям атрибутов MapperProperty
. Оно не применимо к декларативным директивам, таким как __tablename__
, __mapper_args__
и т.д., которые разрешаются внутренним способом, отличным от declared_attr.cascading
.
Объединение аргументов таблицы/маппера из нескольких миксинов¶
В случае __table_args__
или __mapper_args__
, заданных с помощью декларативных миксинов, вы можете захотеть объединить некоторые параметры из нескольких миксинов с теми, которые вы хотите определить в самом классе. Декоратор declared_attr
может быть использован здесь для создания определяемых пользователем процедур свертки, которые берут данные из нескольких коллекций:
from sqlalchemy.orm import declarative_mixin, declared_attr
class MySQLSettings:
__table_args__ = {"mysql_engine": "InnoDB"}
class MyOtherMixin:
__table_args__ = {"info": "foo"}
class MyModel(MySQLSettings, MyOtherMixin, Base):
__tablename__ = "my_model"
@declared_attr
def __table_args__(cls):
args = dict()
args.update(MySQLSettings.__table_args__)
args.update(MyOtherMixin.__table_args__)
return args
id = mapped_column(Integer, primary_key=True)
Создание индексов с помощью миксинов¶
Чтобы определить именованный, потенциально многоколоночный Index
, который применяется ко всем таблицам, полученным из миксина, используйте «inline» форму Index
и установите его как часть __table_args__
:
class MyMixin:
a = mapped_column(Integer)
b = mapped_column(Integer)
@declared_attr
def __table_args__(cls):
return (Index(f"test_idx_{cls.__tablename__}", "a", "b"),)
class MyModel(MyMixin, Base):
__tablename__ = "atable"
c = mapped_column(Integer, primary_key=True)