Значение lazy subquery
Значение lazy='subquery' передается в параметр
lazy функции relationship и включает
стратегию жадной загрузки через подзапрос. При первом
обращении к связанной коллекции SQLAlchemy выполняет
один дополнительный SELECT, который выбирает связанные
объекты для всех родительских строк сразу. Это уменьшает
число запросов по сравнению с lazy='select',
но подзапрос загружает весь набор данных без учета
фильтров и лимитов родительского запроса.
Синтаксис
relationship(SomeModel, lazy='subquery')
Пример
Давайте создадим две связанные модели и установим
для связи значение lazy='subquery':
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
articles: Mapped[list['Article']] = relationship(
back_populates='user',
lazy='subquery'
)
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
user: Mapped['User'] = relationship(back_populates='articles')
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name='user')
user.articles = [Article(title='article 1'), Article(title='article 2')]
session.add(user)
session.commit()
users = session.query(User).all()
for u in users:
print([a.title for a in u.articles])
В логе видно, что после основного SELECT по таблице
users сразу выполняется подзапрос по таблице
articles для всех найденных пользователей:
SELECT users.id AS users_id, users.name AS users_name
FROM users
SELECT articles.user_id AS articles_user_id, articles.id AS articles_id, articles.title AS articles_title
FROM articles
WHERE articles.user_id IN (SELECT users.id FROM users)
Результат выполнения кода:
['article 1', 'article 2']
Пример
Значение lazy='subquery' можно переопределить
на уровне отдельного запроса с помощью опции
lazyload. Давайте загрузим связь лениво,
несмотря на настройку в модели:
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session
from sqlalchemy.orm import lazyload
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = 'users'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
articles: Mapped[list['Article']] = relationship(
back_populates='user',
lazy='subquery'
)
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
user: Mapped['User'] = relationship(back_populates='articles')
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name='user')
user.articles = [Article(title='article 1')]
session.add(user)
session.commit()
users = session.query(User).options(lazyload(User.articles)).all()
for u in users:
print([a.title for a in u.articles])
Результат выполнения кода:
['article 1']
Смотрите также
-
функцию
relationship,
которая настраивает связь между моделями -
значение
lazy select,
которое включает ленивую загрузку связей -
значение
lazy selectin,
которое включает загрузку отдельным IN-запросом -
значение
lazy joined,
которое включает жадную загрузку через JOIN