Функция declared_attr
Функция declared_attr используется в декларативном
стиле SQLAlchemy для определения атрибутов и методов,
которые должны вычисляться динамически при создании
маппированного класса. Она особенно полезна в миксинах
(mixins), где нужно объявить колонки, связи или аргументы
таблицы, зависящие от конкретного класса-наследника.
Функция принимает декорируемый метод, который будет
вызван с объектом класса в качестве первого аргумента
cls. Возвращаемое значение метода становится
значением атрибута в итоговом маппированном классе.
Синтаксис
@declared_attr
def attribute_name(cls):
return value
Пример
Давайте создадим миксин с колонкой status,
используя declared_attr:
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr, Session
class Base(DeclarativeBase):
pass
class StatusMixin:
@declared_attr
def status(cls) -> Mapped[str]:
return mapped_column(String(50), default='draft')
class Article(StatusMixin, Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
article = Article(title='article')
session.add(article)
session.commit()
print(article.status)
Результат выполнения кода:
"draft"
Пример
Давайте используем declared_attr для
динамического определения имени таблицы через
__tablename__:
from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
class Base(DeclarativeBase):
pass
class TableNameMixin:
@declared_attr
def __tablename__(cls) -> str:
return cls.__name__.lower() + 's'
class Article(TableNameMixin, Base):
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
print(Article.__tablename__)
Результат выполнения кода:
"articles"
Пример
Давайте применим declared_attr для
определения связи между моделями:
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, declared_attr, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
class CommentMixin:
@declared_attr
def article_id(cls) -> Mapped[int]:
return mapped_column(ForeignKey('articles.id'))
@declared_attr
def article(cls) -> Mapped[Article]:
return relationship(Article)
class Comment(CommentMixin, Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str]
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
article = Article(title='article')
comment = Comment(text='text', article=article)
session.add(comment)
session.commit()
print(comment.article.title)
Результат выполнения кода:
"article"
Пример
Давайте определим аргументы таблицы через
declared_attr для __table_args__:
from sqlalchemy import create_engine, String, Index
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr
class Base(DeclarativeBase):
pass
class IndexMixin:
@declared_attr
def __table_args__(cls):
return (Index(f'ix_{cls.__tablename__}_title', 'title'),)
class Article(IndexMixin, Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
for index in Article.__table__.indexes:
print(index.name)
Результат выполнения кода:
"ix_articles_title"
Смотрите также
-
класс
DeclarativeBase,
который является базой для декларативных моделей -
функцию
declarative_mixin,
которая помечает класс как миксин для декларативных моделей -
функцию
declarative_base,
которая создает базовый класс для декларативных моделей -
функцию
mapped_column,
которая описывает колонку таблицы в декларативной модели