РЕПЕТИТОР математика физика информатика
Для школьников и студентов. Подтягивание пробелов. ЦЭ, ЦТ, ОГЭ, ЕГЭ.
Идет набор на ЛЕТО. Жмите для подробностей:)
511 of 711 menu

Функция subqueryload

Функция subqueryload включает стратегию жадной загрузки связанных объектов. Она загружает коллекцию или скалярную связь отдельным подзапросом, который выполняется сразу после основного запроса. Первым параметром передаётся атрибут связи, который нужно загрузить. Это избавляет от проблемы N+1 запросов, когда доступ к связи в цикле порождает отдельный SQL-запрос на каждую запись.

Функцию subqueryload удобно применять через опцию options у объекта select. В отличие от joinedload, который соединяет таблицы через JOIN, subqueryload сначала выбирает родительские строки, а затем догружает связанные данные отдельным запросом с оператором IN.

Синтаксис

from sqlalchemy.orm import subqueryload subqueryload(attribute)

Пример

Давайте создадим модели статьи и комментария, связанные отношением один-ко-многим:

from sqlalchemy import ForeignKey from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship class Base(DeclarativeBase): pass 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] 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')

Пример

Давайте заполним базу данными и загрузим статьи вместе с комментариями через subqueryload:

from sqlalchemy import create_engine, select from sqlalchemy.orm import Session, subqueryload from models import Base, Article, Comment engine = create_engine('sqlite:///:memory:') Base.metadata.create_all(engine) with Session(engine) as session: article1 = Article(title='article 1', text='text 1', status='new', num=1) article2 = Article(title='article 2', text='text 2', status='new', num=2) article1.comments = [Comment(text='comment 1'), Comment(text='comment 2')] article2.comments = [Comment(text='comment 3')] session.add_all([article1, article2]) session.commit() stmt = select(Article).options(subqueryload(Article.comments)) res = session.scalars(stmt).all() for article in res: print(article.title, [c.text for c in article.comments])

Результат выполнения кода:

"article 1 ['comment 1', 'comment 2']" "article 2 ['comment 3']"

Пример

Давайте посмотрим, какие SQL-запросы выполняет subqueryload. Основной запрос выбирает статьи, а второй запрос догружает комментарии через IN:

<+python+> from sqlalchemy import create_engine, select from sqlalchemy.orm import Session, subqueryload from models import Base, Article, Comment engine = create_engine('sqlite:///:memory:', echo=True) Base.metadata.create_all(engine) with Session(engine) as session: session.add(Article(title='article 1', text='text 1', status='new', num=1)) session.commit() stmt = select(Article).options(subqueryload(Article.comments)) res = session.scalars(stmt).all() <-python+>

Итоговые SQL-запросы:

SELECT articles.id, articles.title, articles.text, articles.status, articles.num 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 (1)

Пример

Давайте применим subqueryload для обратной связи, загружая статью вместе с комментариями со стороны комментария:

from sqlalchemy import create_engine, select from sqlalchemy.orm import Session, subqueryload from models import Base, Article, Comment engine = create_engine('sqlite:///:memory:') Base.metadata.create_all(engine) with Session(engine) as session: article = Article(title='article 1', text='text 1', status='new', num=1) article.comments = [Comment(text='comment 1')] session.add(article) session.commit() stmt = select(Comment).options(subqueryload(Comment.article)) res = session.scalars(stmt).all() for comment in res: print(comment.text, '->', comment.article.title)

Результат выполнения кода:

"comment 1 -> article 1"

Смотрите также

  • функцию selectinload,
    которая загружает связь запросом с IN
  • функцию joinedload,
    которая загружает связь через JOIN
  • функцию lazyload,
    которая включает ленивую загрузку связи
  • функцию relationship,
    которая настраивает связь между моделями
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить