Значение lazy raise
Значение lazy='raise' указывается в параметре lazy
функции relationship и включает режим, при котором
любое обращение к связи, не загруженной заранее, приводит
к исключению sqlalchemy.exc.InvalidRequestError.
Параметр lazy принимает строковое значение 'raise'.
Такой режим помогает отловить места, где код случайно
обращается к связи и порождает лишние SQL-запросы N+1.
Синтаксис
relationship(..., lazy='raise')
Пример
Давайте создадим две модели со связью в режиме raise
и попробуем обратиться к незагруженному атрибуту:
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(lazy='raise')
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'))
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name='user')
session.add(user)
session.commit()
with Session(engine) as session:
user = session.get(User, 1)
try:
print(user.articles)
except Exception as e:
print(type(e).__name__)
Результат выполнения кода:
"InvalidRequestError"
Пример
Давайте заранее загрузим связь через selectinload
и убедимся, что обращение проходит без ошибки:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session, selectinload
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(lazy='raise')
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'))
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name='user')
user.articles.append(Article(title='article'))
session.add(user)
session.commit()
with Session(engine) as session:
stmt = select(User).options(selectinload(User.articles))
user = session.scalars(stmt).one()
print([a.title for a in user.articles])
Результат выполнения кода:
['article']
Пример
Давайте отключим режим raise на уровне запроса
через raiseload с параметром False:
from sqlalchemy import create_engine, ForeignKey, select
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship, Session, raiseload
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(lazy='raise')
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'))
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
with Session(engine) as session:
user = User(name='user')
user.articles.append(Article(title='article'))
session.add(user)
session.commit()
with Session(engine) as session:
stmt = select(User).options(raiseload(User.articles, False))
user = session.scalars(stmt).one()
print([a.title for a in user.articles])
Результат выполнения кода:
['article']
Смотрите также
-
функцию
relationship,
которая создает связь между моделями -
значение
lazyload,
которое включает обычную отложенную загрузку -
значение
raiseload,
которое управляет ошибкой при загрузке связи -
значение
noload,
которое оставляет связь пустой без запроса