Функция selectin_polymorphic
Функция selectin_polymorphic применяется для
загрузки иерархии наследования моделей. Она принимает
базовый класс и список подклассов. При обращении к
полиморфным атрибутам SQLAlchemy выполняет
дополнительный запрос с SELECT IN вместо
отдельного запроса для каждого подкласса.
Функция полезна при работе с наследованием, когда
требуется загрузить объекты разных типов одной
иерархии без использования joined-загрузки.
Она передается в параметр options метода
query или функции select.
Синтаксис
from sqlalchemy.orm import selectin_polymorphic
selectin_polymorphic(base_class, [subclass1, subclass2])
Пример
Давайте создадим иерархию моделей с наследованием и загрузим полиморфные объекты:
from sqlalchemy import create_engine, ForeignKey, String, Integer
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session
from sqlalchemy.orm import selectin_polymorphic
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))
type: Mapped[str] = mapped_column(String(50))
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'article'
}
class PublishedArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'published'
}
num: Mapped[int] = mapped_column(Integer, nullable=True)
class DraftArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'draft'
}
text: Mapped[str] = mapped_column(String(500), nullable=True)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all([
PublishedArticle(title='article 1', status='active', num=1),
DraftArticle(title='article 2', status='new', text='draft text'),
PublishedArticle(title='article 3', status='active', num=3)
])
session.commit()
res = session.query(Article).options(
selectin_polymorphic(Article, [PublishedArticle, DraftArticle])
).all()
for article in res:
print(type(article).__name__, article.title)
Результат выполнения кода:
PublishedArticle article 1
DraftArticle article 2
PublishedArticle article 3
Пример
Давайте сравним количество запросов при использовании
selectin_polymorphic и без него:
from sqlalchemy import create_engine, String, Integer, event
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
from sqlalchemy.orm import selectin_polymorphic
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
type: Mapped[str] = mapped_column(String(50))
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'article'
}
class PublishedArticle(Article):
__mapper_args__ = {
'polymorphic_identity': 'published'
}
num: Mapped[int] = mapped_column(Integer, nullable=True)
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
query_count = 0
@event.listens_for(engine, 'before_cursor_execute')
def count_queries(conn, cursor, statement, parameters, context, executemany):
global query_count
query_count += 1
with Session(engine) as session:
session.add(PublishedArticle(title='article 1', num=1))
session.commit()
query_count = 0
res = session.query(Article).options(
selectin_polymorphic(Article, [PublishedArticle])
).all()
print('Queries with selectin_polymorphic:', query_count)
Результат выполнения кода:
"Queries with selectin_polymorphic: 2"
Смотрите также
-
функцию
selectinload,
которая загружает связи через SELECT IN -
функцию
joinedload,
которая загружает связи через JOIN -
функцию
subqueryload,
которая загружает связи через подзапрос -
функцию
contains_eager,
которая указывает на уже загруженные связи