Загрузка столбцов¶
В этом разделе представлены дополнительные опции, касающиеся нагружения колонн.
Отложенная загрузка колонн¶
Отложенная загрузка столбцов позволяет загружать определенные столбцы таблицы только при прямом доступе, а не при запросе сущности с помощью 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()
, где она используется для возврата составных атрибутов в качестве объектов при запросе отдельных атрибутов.