Загрузка столбцов

В этом разделе представлены дополнительные опции, касающиеся нагружения колонн.

Отложенная загрузка колонн

Отложенная загрузка столбцов позволяет загружать определенные столбцы таблицы только при прямом доступе, а не при запросе сущности с помощью Query. Эта возможность полезна, когда нужно избежать загрузки в память большого текстового или двоичного поля, когда оно не нужно. Отдельные столбцы можно лениво загружать самостоятельно или объединять в группы, которые лениво загружаются вместе, используя функцию deferred(), чтобы пометить их как «отложенные». В примере ниже мы определяем связку, которая будет загружать каждый из .excerpt и .photo в отдельные, индивидуальные операторы SELECT, когда каждый атрибут впервые упоминается в отдельном экземпляре объекта:

from sqlalchemy.orm import deferred
from sqlalchemy import Integer, String, Text, Binary, Column


class Book(Base):
    __tablename__ = "book"

    book_id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    summary = Column(String(2000))
    excerpt = deferred(Column(Text))
    photo = deferred(Column(Binary))

Классические отображения, как всегда, размещают использование deferred() в словаре properties против связанного с таблицей Column:

mapper_registry.map_imperatively(
    Book, book_table, properties={"photo": deferred(book_table.c.photo)}
)

Отложенные столбцы могут быть связаны с именем «группы», чтобы они загружались вместе при первом обращении к любому из них. Приведенный ниже пример определяет отображение с отложенной группой photos. При обращении к одной из групп .photo все три фотографии будут загружены в одном операторе SELECT. Фотография .excerpt будет загружена отдельно при обращении к ней:

class Book(Base):
    __tablename__ = "book"

    book_id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    summary = Column(String(2000))
    excerpt = deferred(Column(Text))
    photo1 = deferred(Column(Binary), group="photos")
    photo2 = deferred(Column(Binary), group="photos")
    photo3 = deferred(Column(Binary), group="photos")

Параметры запроса отложенного загрузчика колонок

Колонки могут быть помечены как «отложенные» или сброшены в «не отложенные» во время запроса с помощью опций, которые передаются в метод Query.options(); самые основные опции запроса - defer() и undefer():

from sqlalchemy.orm import defer
from sqlalchemy.orm import undefer

query = session.query(Book)
query = query.options(defer("summary"), undefer("excerpt"))
query.all()

Выше, колонка «summary» не будет загружаться до тех пор, пока к ней не обратятся, а колонка «excerpt» будет загружена немедленно, даже если она была отображена как «отложенная».

Атрибуты deferred(), помеченные «группой», могут быть отложены с помощью undefer_group(), передавая в имени группы:

from sqlalchemy.orm import undefer_group

query = session.query(Book)
query.options(undefer_group("photos")).all()

Отложенная загрузка по нескольким предприятиям

Чтобы задать отсрочку столбцов для Query, который загружает несколько типов сущностей одновременно, параметры отсрочки можно задать более явно, используя атрибуты, связанные с классом, а не строковые имена:

from sqlalchemy.orm import defer

query = session.query(Book, Author).join(Book.author)
query = query.options(defer(Author.bio))

Опции отсрочки колонок могут также указывать, что они происходят по различным путям отношений, которые сами часто eagerly loaded с опциями загрузчика. Все связанные с отношениями опции загрузчика поддерживают цепочку на дополнительные опции загрузчика, которые включают загрузку для дальнейших уровней отношений, а также на ориентированные на столбцы атрибуты на этом пути. Например, для загрузки экземпляров Author, затем joined-eager-загрузки коллекции Author.books для каждого автора, затем применения опций отсрочки к атрибутам, ориентированным на столбцы, к каждой сущности Book из этого отношения, опция загрузчика joinedload() может быть объединена с опцией load_only() (описанной далее в этом разделе) для отсрочки всех столбцов Book, кроме явно указанных:

from sqlalchemy.orm import joinedload

query = session.query(Author)
query = query.options(
    joinedload(Author.books).load_only(Book.summary, Book.excerpt),
)

Структуры опций, описанные выше, могут быть организованы более сложным образом, например, иерархически с помощью метода Load.options(), который позволяет одновременно связывать несколько подвариантов с общей родительской опцией. Можно использовать любую смесь строковых имен и связанных с классом объектов-атрибутов:

from sqlalchemy.orm import defer
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import load_only

query = session.query(Author)
query = query.options(
    joinedload(Author.book).options(
        load_only(Book.summary, Book.excerpt),
        joinedload(Book.citations).options(
            joinedload(Citation.author), defer(Citation.fulltext)
        ),
    )
)

Добавлено в версии 1.3.6: Добавлено Load.options() для упрощения построения иерархий опций загрузчика.

Другим способом применения опций к пути является использование функции defaultload(). Эта функция используется для указания конкретного пути в структуре опций загрузчика без фактической установки каких-либо опций на этом уровне, чтобы можно было применять дальнейшие под-опции. Функция defaultload() может быть использована для создания такой же структуры, как мы делали выше, используя Load.options() как:

query = session.query(Author)
query = query.options(
    joinedload(Author.book).load_only(Book.summary, Book.excerpt),
    defaultload(Author.book).joinedload(Book.citations).joinedload(Citation.author),
    defaultload(Author.book).defaultload(Book.citations).defer(Citation.fulltext),
)

См.также

Загрузка отношений с помощью опций загрузчика - направлена на загрузку отношений

Опции «Только загрузка» и «Подстановочный знак

Система опций загрузчика ORM поддерживает концепцию «подстановочных» опций загрузчика, в которой опции загрузчика может быть передана звездочка "*", чтобы указать, что определенная опция должна применяться ко всем применимым атрибутам сопоставленного класса. Например, если мы хотим загрузить класс Book, но только столбцы «summary» и «excerpt», мы можем сказать:

from sqlalchemy.orm import defer
from sqlalchemy.orm import undefer

session.query(Book).options(defer("*"), undefer("summary"), undefer("excerpt"))

Выше, опция defer() применяется с использованием подстановочного знака ко всем атрибутам столбцов класса Book. Затем опция undefer() применяется к полям «summary» и «excerpt», чтобы они были единственными колонками, загружаемыми вперед. Запрос к приведенной выше сущности будет включать в SELECT только поля «summary» и «excerpt», а также столбцы первичного ключа, которые всегда используются ORM.

Аналогичная функция доступна с меньшей многословностью при использовании опции load_only(). Это так называемая исключительная опция, которая будет применять отложенное поведение ко всем атрибутам столбцов, кроме тех, которые имеют имя:

from sqlalchemy.orm import load_only

session.query(Book).options(load_only(Book.summary, Book.excerpt))

Подстановочные знаки и опции исключения в запросах с несколькими сущностями

Опции подстановочных знаков и исключающие опции, такие как load_only(), могут применяться только к одной сущности одновременно в рамках Query. Для менее распространенного случая, когда Query возвращает сразу несколько первичных сущностей, может потребоваться специальный стиль вызова для применения подстановочного знака или исключающей опции, который заключается в использовании объекта Load для указания начальной сущности для опции отсрочки. Например, если мы загружаем Book и Author одновременно, то Query вызовет информационную ошибку, если мы попытаемся применить load_only() сразу к обоим. Использование Load выглядит следующим образом:

from sqlalchemy.orm import Load

query = session.query(Book, Author).join(Book.author)
query = query.options(Load(Book).load_only(Book.summary, Book.excerpt))

Выше Load используется в сочетании с исключающей опцией load_only(), так что откладывание всех остальных столбцов происходит только для класса Book, но не для класса Author. Опять же, объект Query должен выдавать информативное сообщение об ошибке, когда на самом деле требуется вышеуказанный стиль вызова, описывающее те случаи, когда необходимо явное использование Load.

Перегрузка для отложенных колонок

Добавлено в версии 1.4.

Опция загрузчика deferred() и соответствующая стратегия загрузчика также поддерживают концепцию «raiseload», которая представляет собой стратегию загрузчика, которая будет поднимать InvalidRequestError, если к атрибуту обращаются так, что для его загрузки потребуется выполнить SQL-запрос. Это поведение является эквивалентом функции raiseload() для загрузки отношений на основе столбцов, о которой говорилось в Предотвращение нежелательных ленивых загрузок с помощью raiseload. При использовании параметра defer.raiseload в опции defer(), если доступ к атрибуту осуществляется:

book = session.query(Book).options(defer(Book.summary, raiseload=True)).first()

# would raise an exception
book.summary

Отложенная «raiseload» может быть настроена на уровне маппера через deferred.raiseload на deferred(), так что для использования атрибута требуется явное undefer():

class Book(Base):
    __tablename__ = "book"

    book_id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    summary = deferred(Column(String(2000)), raiseload=True)
    excerpt = deferred(Column(Text), raiseload=True)


book_w_excerpt = session.query(Book).options(undefer(Book.excerpt)).first()

API отсрочки колонок

Пучки колонн

Bundle может использоваться для запроса групп столбцов в одном пространстве имен.

Пакет позволяет группировать колонки:

from sqlalchemy.orm import Bundle

bn = Bundle("mybundle", MyClass.data1, MyClass.data2)
for row in session.query(bn).filter(bn.c.data1 == "d1"):
    print(row.mybundle.data1, row.mybundle.data2)

Пакет может быть подклассифицирован для обеспечения пользовательского поведения при получении результатов. Во время выполнения запроса методу Bundle.create_row_processor() передается объект statement и набор функций «row processor»; эти функции при получении строки результата возвращают значение отдельного атрибута, которое затем может быть адаптировано в любую структуру возвращаемых данных. Ниже показана замена обычной структуры возврата Row на прямой словарь Python:

from sqlalchemy.orm import Bundle


class DictBundle(Bundle):
    def create_row_processor(self, query, procs, labels):
        """Override create_row_processor to return values as dictionaries"""

        def proc(row):
            return dict(zip(labels, (proc(row) for proc in procs)))

        return proc

Примечание

Конструкция Bundle применяется только к выражениям столбцов. Она не применяется к атрибутам ORM, сопоставленным с помощью relationship().

Изменено в версии 1.0: Вызываемая переменная proc(), передаваемая методу create_row_processor() пользовательских классов Bundle, теперь принимает только один аргумент «row».

Результат от вышеуказанной связки вернет словарные значения:

bn = DictBundle("mybundle", MyClass.data1, MyClass.data2)
for row in session.query(bn).filter(bn.c.data1 == "d1"):
    print(row.mybundle["data1"], row.mybundle["data2"])

Конструкция Bundle также интегрирована в поведение composite(), где она используется для возврата составных атрибутов в качестве объектов при запросе отдельных атрибутов.

Вернуться на верх