Функция contains_eager
Функция contains_eager сообщает ORM,
что связанные объекты уже загружены через
явный JOIN в запросе, и не нужно
выполнять отдельный запрос для связи.
Первым параметром передается атрибут связи,
вторым необязательным параметром можно
указать псевдоним для вложенных связей.
Функция полезна, когда вы сами пишете
join и хотите избежать
дополнительных запросов ленивой загрузки.
Синтаксис
from sqlalchemy.orm import contains_eager
contains_eager(attribute, [alias])
Пример
Давайте опишем две модели: статью и пользователя-автора:
from sqlalchemy import ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
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')
class Article(Base):
__tablename__ = 'articles'
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
text: Mapped[str]
status: Mapped[str]
num: Mapped[int]
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
user: Mapped['User'] = relationship(back_populates='articles')
Пример
Давайте заполним базу и выполним запрос
с явным join и contains_eager:
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, contains_eager
from models import Base, User, Article
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', text='text 1', status='ok', num=1),
Article(title='article 2', text='text 2', status='ok', num=2),
]
session.add(user)
session.commit()
stmt = (
select(Article)
.join(Article.user)
.options(contains_eager(Article.user))
)
res = session.scalars(stmt).all()
for article in res:
print(article.title, '->', article.user.name)
Результат выполнения кода:
"article 1 -> user"
"article 2 -> user"
Пример
Давайте посмотрим SQL, который генерирует
contains_eager - связь
загружается одним запросом:
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, contains_eager
from models import Base, Article
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
stmt = (
select(Article)
.join(Article.user)
.options(contains_eager(Article.user))
)
print(stmt)
Итоговый SQL запрос:
SELECT articles.id, articles.title, articles.text,
articles.status, articles.num, articles.user_id,
users_1.id AS id_1, users_1.name
FROM articles
JOIN users AS users_1 ON users_1.id = articles.user_id
Смотрите также
-
функцию
joinedload,
которая тоже загружает связь через JOIN -
функцию
selectinload,
которая загружает связь отдельным запросом -
функцию
subqueryload,
которая загружает связь через подзапрос -
функцию
relationship,
которая описывает связь между моделями