Атрибут polymorphic_identity класса Mapper
задает строковый идентификатор для полиморфного
подкласса при наследовании моделей. Этот
идентификатор сохраняется в столбце, указанном
в атрибуте polymorphic_on, и позволяет
SQLAlchemy при выборке строки из таблицы
определить, какому классу-наследнику она
принадлежит. Значением атрибута может быть
любая строка или целое число, однозначно
идентифицирующее подкласс.
Синтаксис
__mapper_args__ = {'polymorphic_identity': 'identity_value'}
Пример
Давайте создадим базовый класс Article и
подкласс DraftArticle, указав для
подкласса идентификатор draft:
<+python+>
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
status: Mapped[str] = mapped_column(String(50))
__mapper_args__ = {
'polymorphic_on': status,
'polymorphic_identity': 'article'
}
class DraftArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'draft'
}
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
draft = DraftArticle(title='article', status='draft')
session.add(draft)
session.commit()
res = session.query(Article).all()
for article in res:
print(type(article).__name__, article.title, article.status)
<-python+>
Результат выполнения кода:
"DraftArticle article draft"
Пример
Давайте добавим второй подкласс PublishedArticle
с идентификатором published и убедимся,
что SQLAlchemy корректно различает типы:
<+python+>
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
status: Mapped[str] = mapped_column(String(50))
__mapper_args__ = {
'polymorphic_on': status,
'polymorphic_identity': 'article'
}
class DraftArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'draft'
}
class PublishedArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'published'
}
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(DraftArticle(title='article', status='draft'))
session.add(PublishedArticle(title='article', status='published'))
session.commit()
res = session.query(Article).order_by(Article.id).all()
for article in res:
print(type(article).__name__, article.status)
<-python+>
Результат выполнения кода:
"DraftArticle draft"
"PublishedArticle published"
Пример
Давайте посмотрим, какой SQL-запрос генерирует
SQLAlchemy при выборке полиморфных объектов:
<+python+>
from sqlalchemy import create_engine, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
status: Mapped[str] = mapped_column(String(50))
__mapper_args__ = {
'polymorphic_on': status,
'polymorphic_identity': 'article'
}
class DraftArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'draft'
}
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(DraftArticle(title='article', status='draft'))
session.commit()
res = session.query(DraftArticle).all()
print(len(res))
<-python+>
Итоговый SQL запрос:
SELECT articles.id AS articles_id, articles.title AS articles_title, articles.status AS articles_status
FROM articles
WHERE articles.status IN (?)
Смотрите также
-
атрибут
polymorphic_on,
который указывает столбец-дискриминатор
-
класс
Mapper,
который представляет отображение класса
-
атрибут
class_,
который хранит отображаемый класс
-
атрибут
columns,
который содержит коллекцию столбцов