Функция selectinload
Функция selectinload выполняет жадную загрузку
связанных объектов, отправляя дополнительный
SELECT-запрос с условием IN по первичным ключам
родительских записей. Первым параметром функция
принимает атрибут отношения, который нужно загрузить.
В отличие от joinedload, она не использует
JOIN и поэтому не раздувает результирующий набор
при загрузке коллекций.
Синтаксис
selectinload(attr)
Пример
Давайте создадим модели статьи и комментария, связанные отношением один-ко-многим:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
comments: Mapped[list['Comment']] = relationship(back_populates='article')
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str]
article_id: Mapped[int] = mapped_column(ForeignKey('articles.id'))
article: Mapped['Article'] = relationship(back_populates='comments')
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
article = Article(title='article', comments=[
Comment(text='article'),
Comment(text='article'),
])
session.add(article)
session.commit()
Пример
Давайте загрузим статьи вместе с комментариями
через selectinload:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session, selectinload
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
comments: Mapped[list['Comment']] = relationship(back_populates='article')
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str]
article_id: Mapped[int] = mapped_column(ForeignKey('articles.id'))
article: Mapped['Article'] = relationship(back_populates='comments')
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Article(title='article', comments=[
Comment(text='article'),
Comment(text='article'),
]))
session.commit()
stmt = select(Article).options(selectinload(Article.comments))
res = session.scalars(stmt).all()
for article in res:
print(article.title, [c.text for c in article.comments])
Результат выполнения кода:
"article ['article', 'article']"
Пример
Давайте посмотрим, какие SQL-запросы выполняет
selectinload при загрузке коллекции:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session, selectinload
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
comments: Mapped[list['Comment']] = relationship(back_populates='article')
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str]
article_id: Mapped[int] = mapped_column(ForeignKey('articles.id'))
article: Mapped['Article'] = relationship(back_populates='comments')
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Article(title='article', comments=[
Comment(text='article'),
Comment(text='article'),
]))
session.commit()
stmt = select(Article).options(selectinload(Article.comments))
res = session.scalars(stmt).all()
Итоговые SQL-запросы:
SELECT articles.id, articles.title
FROM articles
SELECT comments.article_id AS comments_article_id, comments.id AS comments_id, comments.text AS comments_text
FROM comments
WHERE comments.article_id IN (?)
Пример
Давайте загрузим связанный объект со стороны
многих-к-одному через selectinload:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session, selectinload
class Base(DeclarativeBase):
pass
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
comments: Mapped[list['Comment']] = relationship(back_populates='article')
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str]
article_id: Mapped[int] = mapped_column(ForeignKey('articles.id'))
article: Mapped['Article'] = relationship(back_populates='comments')
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Article(title='article', comments=[
Comment(text='article'),
]))
session.commit()
stmt = select(Comment).options(selectinload(Comment.article))
res = session.scalars(stmt).all()
for comment in res:
print(comment.text, comment.article.title)
Результат выполнения кода:
"article article"
Смотрите также
-
функцию
joinedload,
которая загружает связи через LEFT OUTER JOIN -
функцию
subqueryload,
которая загружает связи отдельным подзапросом -
функцию
lazyload,
которая загружает связи лениво при обращении -
функцию
relationship,
которая настраивает отношения между моделями