Значение lazy dynamic
Значение lazy="dynamic" указывается в атрибуте
relationship и меняет поведение загрузки
связанных объектов. Вместо списка при обращении
к атрибуту возвращается объект Query,
который можно донастраивать: фильтровать,
сортировать и ограничивать количество записей.
Это удобно для коллекций с большим числом
связанных строк, когда не нужно загружать их все
сразу.
Параметр lazy принимает строковое значение
"dynamic". При этом сам атрибут перестаёт
вести себя как обычный список: к нему нельзя
применить индексацию или срез напрямую, но можно
вызвать методы filter, order_by,
limit и другие. Итоговая выборка выполняется
только в момент итерации или вызова методов
вроде all и count.
Синтаксис
relationship(..., lazy="dynamic")
Пример
Давайте опишем модель статьи и связь с
комментариями через lazy="dynamic":
from sqlalchemy import create_engine, ForeignKey, String, Text
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] = mapped_column(String(100))
text: Mapped[str] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(50))
num: Mapped[int]
comments: Mapped[list['Comment']] = relationship(
back_populates='article',
lazy='dynamic',
)
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str] = mapped_column(Text)
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',
text='article text',
status='published',
num=1,
)
article.comments = [
Comment(text='first'),
Comment(text='second'),
]
session.add(article)
session.commit()
print(type(article.comments))
Результат выполнения кода:
<class 'sqlalchemy.orm.dynamic.AppenderQuery'>
Пример
Давайте применим фильтрацию к динамической связи и получим только нужные комментарии:
from sqlalchemy import create_engine, ForeignKey, String, Text
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] = mapped_column(String(100))
text: Mapped[str] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(50))
num: Mapped[int]
comments: Mapped[list['Comment']] = relationship(
back_populates='article',
lazy='dynamic',
)
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str] = mapped_column(Text)
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',
text='article text',
status='published',
num=1,
)
article.comments = [
Comment(text='first'),
Comment(text='second'),
Comment(text='third'),
]
session.add(article)
session.commit()
res = article.comments.filter(Comment.text != 'second').all()
print([comment.text for comment in res])
Результат выполнения кода:
['first', 'third']
Пример
Давайте посчитаем количество связанных комментариев без их полной загрузки:
from sqlalchemy import create_engine, ForeignKey, String, Text
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] = mapped_column(String(100))
text: Mapped[str] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(50))
num: Mapped[int]
comments: Mapped[list['Comment']] = relationship(
back_populates='article',
lazy='dynamic',
)
class Comment(Base):
__tablename__ = 'comments'
id: Mapped[int] = mapped_column(primary_key=True)
text: Mapped[str] = mapped_column(Text)
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',
text='article text',
status='published',
num=1,
)
article.comments = [
Comment(text='first'),
Comment(text='second'),
]
session.add(article)
session.commit()
print(article.comments.count())
Результат выполнения кода:
2
Смотрите также
-
атрибут
relationship,
который настраивает связи между моделями -
значение
lazy="select",
которое загружает связь отдельным запросом -
значение
lazy="joined",
которое загружает связь через JOIN -
значение
lazy="selectin",
которое загружает связь пакетом через IN