SQLAlchemy 2.0 Dokumentation
SQLAlchemy ORM
- ORM Schnellstart
- ORM Abgebildete Klassenkonfiguration
- Beziehungskonfiguration
- ORM Abfragehandbuch
- Schreiben von SELECT-Anweisungen für ORM-zugeordnete Klassen
- Schreiben von SELECT-Anweisungen für Vererbungszuordnungen
- ORM-aktivierte INSERT-, UPDATE- und DELETE-Anweisungen
- Spaltenladeoptionen¶
- Begrenzung der zu ladenden Spalten mit Spaltengruppierung
- Spaltenverzögerung auf Mappings konfigurieren
- Verwendung von
deferred()für imperative Mapper, zugeordnete SQL-Ausdrücke - Verwendung von
undefer()zum "Eager" Laden von deferrten Spalten - Verzögerte Spalten in Gruppen laden
- Aufheben der Gruppierung mit
undefer_group() - Aufheben der Gruppierung mit Platzhaltern
- Konfiguration des Mapper-Level-Verhaltens "raiseload"
- Verwendung von
- Laden beliebiger SQL-Ausdrücke in Objekte
- Spaltenlade-API
- Techniken für das Laden von Beziehungen
- ORM-API-Funktionen für Abfragen
- Legacy Query API
- Verwendung der Sitzung
- Ereignisse und Interna
- ORM Erweiterungen
- ORM Beispiele
Projektversionen
- Vorher: ORM-fähige INSERT-, UPDATE- und DELETE-Anweisungen
- Nächste: Techniken zum Laden von Beziehungen
- Oben: Startseite
- Auf dieser Seite
- Spaltenladeoptionen
- Begrenzung der zu ladenden Spalten mit Spaltengruppierung
- Spaltenverzögerung auf Mappings konfigurieren
- Verwendung von
deferred()für imperative Mapper, zugeordnete SQL-Ausdrücke - Verwendung von
undefer()zum "Eager" Laden von deferrten Spalten - Verzögerte Spalten in Gruppen laden
- Aufheben der Gruppierung mit
undefer_group() - Aufheben der Gruppierung mit Platzhaltern
- Konfiguration des Mapper-Level-Verhaltens "raiseload"
- Verwendung von
- Laden beliebiger SQL-Ausdrücke in Objekte
- Spaltenlade-API
Spaltenladeoptionen¶
Über dieses Dokument
Dieser Abschnitt stellt zusätzliche Optionen bezüglich des Ladens von Spalten vor. Die verwendeten Mappings umfassen Spalten, die große Zeichenkettenwerte speichern würden, für die wir möglicherweise einschränken möchten, wann sie geladen werden.
ORM-Setup für diese Seite anzeigen. Einige der unten stehenden Beispiele werden den Book-Mapper neu definieren, um einige der Spaltendefinitionen zu ändern.
Begrenzung der zu ladenden Spalten mit Spaltengruppierung¶
Spaltengruppierung bezieht sich auf ORM-zugeordnete Spalten, die von einer SELECT-Anweisung weggelassen werden, wenn Objekte dieses Typs abgefragt werden. Die allgemeine Begründung hierfür ist die Leistung, in Fällen, in denen Tabellen selten verwendete Spalten mit potenziell großen Datenwerten haben, da das vollständige Laden dieser Spalten bei jeder Abfrage zeit- und/oder speicherintensiv sein kann. SQLAlchemy ORM bietet eine Vielzahl von Möglichkeiten, das Laden von Spalten zu steuern, wenn Entitäten geladen werden.
Die meisten Beispiele in diesem Abschnitt veranschaulichen ORM-Ladeoptionen. Dies sind kleine Konstrukte, die an die Methode Select.options() des Objekts Select übergeben werden, welche dann vom ORM verbraucht werden, wenn das Objekt in eine SQL-Zeichenkette kompiliert wird.
Verwendung von load_only() zur Reduzierung geladener Spalten¶
Die Ladeoption load_only() ist die zweckmäßigste Option, wenn Objekte geladen werden, bei denen nur eine kleine Handvoll Spalten zugänglich sein wird. Diese Option akzeptiert eine variable Anzahl von klassenbasierten Attributobjekten, die die spaltengemanschten Attribute angeben, die geladen werden sollen, wobei alle anderen spaltengemanschten Attribute außerhalb des Primärschlüssels nicht Teil der abgerufenen Spalten sind. Im folgenden Beispiel enthält die Klasse Book die Spalten .title, .summary und .cover_photo. Mit load_only() können wir das ORM anweisen, nur die Spalten .title und .summary vorab zu laden.
>>> from sqlalchemy import select
>>> from sqlalchemy.orm import load_only
>>> stmt = select(Book).options(load_only(Book.title, Book.summary))
>>> books = session.scalars(stmt).all()
SELECT book.id, book.title, book.summary
FROM book
[...] ()
>>> for book in books:
... print(f"{book.title} {book.summary}")
100 Years of Krabby Patties some long summary
Sea Catch 22 another long summary
The Sea Grapes of Wrath yet another summary
A Nut Like No Other some long summary
Geodesic Domes: A Retrospective another long summary
Rocketry for Squirrels yet another summaryOben hat die SELECT-Anweisung die Spalte .cover_photo weggelassen und nur .title und .summary sowie die Primärschlüsselspalte .id eingeschlossen; das ORM ruft typischerweise immer die Primärschlüsselspalten ab, da diese erforderlich sind, um die Identität für die Zeile festzustellen.
Nach dem Laden weist das Objekt normalerweise ein Lazy Loading-Verhalten für die verbleibenden ungeladenen Attribute auf, was bedeutet, dass bei jedem ersten Zugriff eine SQL-Anweisung innerhalb der aktuellen Transaktion gesendet wird, um den Wert zu laden. Unten emittiert der Zugriff auf .cover_photo eine SELECT-Anweisung, um seinen Wert zu laden.
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (1,)
Lazy Loads werden immer über die Session emittiert, der das Objekt im persistenten Zustand ist. Wenn das Objekt von einer Session detached ist, schlägt die Operation fehl und löst eine Ausnahme aus.
Als Alternative zum Lazy Loading beim Zugriff können deferrte Spalten auch so konfiguriert werden, dass beim Zugriff eine informative Ausnahme ausgelöst wird, unabhängig von ihrem Anbindungsstatus. Bei Verwendung des Konstrukts load_only() kann dies über den Parameter load_only.raiseload angezeigt werden. Hintergrundinformationen und Beispiele finden Sie im Abschnitt Verwendung von raiseload zur Verhinderung von deferrten Spaltenladungen.
Tipp
Wie anderswo angemerkt, ist Lazy Loading bei der Verwendung von Asynchronem I/O (asyncio) nicht verfügbar.
Verwendung von load_only() mit mehreren Entitäten¶
load_only() beschränkt sich auf die einzelne Entität, auf die in ihrer Attributliste verwiesen wird (das Übergeben einer Liste von Attributen, die mehr als eine einzelne Entität umfassen, ist derzeit nicht zulässig). Im folgenden Beispiel gilt die angegebene load_only()-Option nur für die Entität Book. Die ebenfalls ausgewählte Entität User ist nicht betroffen; in der resultierenden SELECT-Anweisung sind alle Spalten für user_account vorhanden, während für die Tabelle book nur book.id und book.title vorhanden sind.
>>> stmt = select(User, Book).join_from(User, Book).options(load_only(Book.title))
>>> print(stmt)
SELECT user_account.id, user_account.name, user_account.fullname,
book.id AS id_1, book.title
FROM user_account JOIN book ON user_account.id = book.owner_id
Wenn wir load_only()-Optionen sowohl für User als auch für Book anwenden möchten, würden wir zwei separate Optionen verwenden.
>>> stmt = (
... select(User, Book)
... .join_from(User, Book)
... .options(load_only(User.name), load_only(Book.title))
... )
>>> print(stmt)
SELECT user_account.id, user_account.name, book.id AS id_1, book.title
FROM user_account JOIN book ON user_account.id = book.owner_id
Verwendung von load_only() für verwandte Objekte und Sammlungen¶
Bei Verwendung von Relationship-Ladern zur Steuerung des Ladens von verwandten Objekten kann die Methode Load.load_only() eines beliebigen Relationship-Laders verwendet werden, um load_only()-Regeln auf Spalten der Unterentität anzuwenden. Im folgenden Beispiel wird selectinload() verwendet, um die verwandte books-Sammlung auf jedem User-Objekt zu laden. Durch Anwendung von Load.load_only() auf das resultierende Options-Objekt werden beim Laden von Objekten für die Beziehung nur die Spalte title neben der Primärschlüsselspalte geladen.
>>> from sqlalchemy.orm import selectinload
>>> stmt = select(User).options(selectinload(User.books).load_only(Book.title))
>>> for user in session.scalars(stmt):
... print(f"{user.fullname} {[b.title for b in user.books]}")
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
[...] ()
SELECT book.owner_id AS book_owner_id, book.id AS book_id, book.title AS book_title
FROM book
WHERE book.owner_id IN (?, ?)
[...] (1, 2)
Spongebob Squarepants ['100 Years of Krabby Patties', 'Sea Catch 22', 'The Sea Grapes of Wrath']
Sandy Cheeks ['A Nut Like No Other', 'Geodesic Domes: A Retrospective', 'Rocketry for Squirrels']load_only() kann auch auf Unterentitäten angewendet werden, ohne den Ladestil für die Beziehung selbst angeben zu müssen. Wenn wir den Standardladestil von User.books nicht ändern möchten, aber dennoch Lade-Nur-Regeln auf Book anwenden möchten, würden wir die Option defaultload() verwenden, die in diesem Fall den Standardladestil der Beziehung "lazy" beibehält und unsere benutzerdefinierte load_only()-Regel auf die für jede User.books-Sammlung gesendete SELECT-Anweisung anwendet.
>>> from sqlalchemy.orm import defaultload
>>> stmt = select(User).options(defaultload(User.books).load_only(Book.title))
>>> for user in session.scalars(stmt):
... print(f"{user.fullname} {[b.title for b in user.books]}")
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
[...] ()
SELECT book.id AS book_id, book.title AS book_title
FROM book
WHERE ? = book.owner_id
[...] (1,)
Spongebob Squarepants ['100 Years of Krabby Patties', 'Sea Catch 22', 'The Sea Grapes of Wrath']
SELECT book.id AS book_id, book.title AS book_title
FROM book
WHERE ? = book.owner_id
[...] (2,)
Sandy Cheeks ['A Nut Like No Other', 'Geodesic Domes: A Retrospective', 'Rocketry for Squirrels']Verwendung von defer() zum Auslassen spezifischer Spalten¶
Die Ladeoption defer() ist eine feinere Alternative zu load_only(), die es ermöglicht, eine einzelne spezifische Spalte als "nicht laden" zu markieren. Im folgenden Beispiel wird defer() direkt auf die Spalte .cover_photo angewendet, während das Verhalten aller anderen Spalten unverändert bleibt.
>>> from sqlalchemy.orm import defer
>>> stmt = select(Book).where(Book.owner_id == 2).options(defer(Book.cover_photo))
>>> books = session.scalars(stmt).all()
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.owner_id = ?
[...] (2,)
>>> for book in books:
... print(f"{book.title}: {book.summary}")
A Nut Like No Other: some long summary
Geodesic Domes: A Retrospective: another long summary
Rocketry for Squirrels: yet another summaryWie bei load_only() werden ungeladene Spalten standardmäßig geladen, wenn sie über Lazy Loading abgerufen werden.
>>> img_data = books[0].cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (4,)
Mehrere defer()-Optionen können in einer Anweisung verwendet werden, um mehrere Spalten als deferrt zu markieren.
Wie bei load_only() enthält die Option defer() ebenfalls die Möglichkeit, dass ein deferrtes Attribut beim Zugriff eine Ausnahme auslöst, anstatt Lazy Loading. Dies wird im Abschnitt Verwendung von raiseload zur Verhinderung von deferrten Spaltenladungen veranschaulicht.
Verwendung von raiseload zur Verhinderung von deferrten Spaltenladungen¶
Bei Verwendung der Ladeoptionen load_only() oder defer() haben Attribute, die als deferrt auf einem Objekt markiert sind, das Standardverhalten, dass beim ersten Zugriff eine SELECT-Anweisung innerhalb der aktuellen Transaktion gesendet wird, um ihren Wert zu laden. Es ist oft notwendig, diese Ladung zu verhindern und stattdessen eine Ausnahme auszulösen, wenn auf das Attribut zugegriffen wird, was darauf hinweist, dass die Notwendigkeit, die Datenbank nach dieser Spalte abzufragen, nicht erwartet wurde. Ein typisches Szenario ist eine Operation, bei der Objekte mit allen Spalten geladen werden, die für den ordnungsgemäßen Ablauf der Operation als erforderlich bekannt sind und die dann an eine View-Schicht weitergegeben werden. Alle weiteren SQL-Operationen, die in der View-Schicht ausgelöst werden, sollten abgefangen werden, damit die vorab erfolgte Ladeoperation angepasst werden kann, um diese zusätzlichen Daten vorab zu berücksichtigen, anstatt zusätzliche Lazy Loads zu verursachen.
Für diesen Anwendungsfall enthalten die Optionen defer() und load_only() einen booleschen Parameter defer.raiseload, der, wenn er auf True gesetzt ist, dazu führt, dass die betroffenen Attribute beim Zugriff fehlschlagen. Im folgenden Beispiel erlaubt die deferrte Spalte .cover_photo keinen Attributzugriff.
>>> book = session.scalar(
... select(Book).options(defer(Book.cover_photo, raiseload=True)).where(Book.id == 4)
... )
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.id = ?
[...] (4,)
>>> book.cover_photo
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.cover_photo' is not available due to raiseload=TrueBei Verwendung von load_only() zur Benennung einer bestimmten Menge von nicht-deferrten Spalten kann das raiseload-Verhalten für die übrigen Spalten über den Parameter load_only.raiseload angewendet werden, der auf alle deferrten Attribute angewendet wird.
>>> session.expunge_all()
>>> book = session.scalar(
... select(Book).options(load_only(Book.title, raiseload=True)).where(Book.id == 5)
... )
SELECT book.id, book.title
FROM book
WHERE book.id = ?
[...] (5,)
>>> book.summary
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.summary' is not available due to raiseload=TrueHinweis
Es ist noch nicht möglich, load_only() und defer()-Optionen, die sich auf dieselbe Entität beziehen, gemeinsam in einer Anweisung zu mischen, um das raiseload-Verhalten bestimmter Attribute zu ändern; derzeit führt dies zu einem undefinierten Ladeverhalten von Attributen.
Siehe auch
Das Feature defer.raiseload ist die Spalten-Level-Version desselben "raiseload"-Features, das für Beziehungen verfügbar ist. Für "raiseload" mit Beziehungen siehe Verhinderung unerwünschter Lazy Loads mit raiseload im Abschnitt Techniken zum Laden von Beziehungen dieses Leitfadens.
Konfiguration der Spaltengruppierung auf Mapping-Ebene¶
Die Funktionalität von defer() ist als Standardverhalten für zugeordnete Spalten verfügbar, da dies für Spalten angemessen sein kann, die nicht bedingungslos bei jeder Abfrage geladen werden sollten. Zur Konfiguration verwenden Sie den Parameter mapped_column.deferred von mapped_column(). Das folgende Beispiel veranschaulicht ein Mapping für Book, das die standardmäßige Spaltengruppierung auf die Spalten summary und cover_photo anwendet.
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(Text, deferred=True)
... cover_photo: Mapped[bytes] = mapped_column(LargeBinary, deferred=True)
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"Mit dem obigen Mapping schließen Abfragen gegen Book automatisch die Spalten summary und cover_photo aus.
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
Wie bei jeder Gruppierung ist das Standardverhalten, wenn die deferrten Attribute im geladenen Objekt zum ersten Mal aufgerufen werden, dass sie ihren Wert per Lazy Load laden.
>>> img_data = book.cover_photo
SELECT book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
Wie bei den Ladeoptionen defer() und load_only() enthält die Mapper-Level-Gruppierung auch eine Option für das raiseload-Verhalten, anstatt Lazy Loading, wenn keine anderen Optionen in einer Anweisung vorhanden sind. Dies ermöglicht ein Mapping, bei dem bestimmte Spalten standardmäßig nicht geladen werden und auch nie lazy geladen werden, ohne explizite Anweisungen in einer Anweisung. Weitere Informationen zur Konfiguration und Verwendung dieses Verhaltens finden Sie im Abschnitt Konfiguration des Mapper-Level-Verhaltens "raiseload".
Verwendung von deferred() für imperative Mapper, zugeordnete SQL-Ausdrücke¶
Die Funktion deferred() ist die frühere, allgemeinere Direktive für "deferrte Spaltenzuordnung", die der Einführung des Konstrukts mapped_column() in SQLAlchemy vorausgeht.
deferred() wird bei der Konfiguration von ORM-Mappern verwendet und akzeptiert beliebige SQL-Ausdrücke oder Column-Objekte. Daher eignet es sich für die Verwendung mit nicht-deklarativen imperativen Mappings und kann dem Wörterbuch map_imperatively.properties übergeben werden.
from sqlalchemy import Blob
from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Text
from sqlalchemy.orm import registry
mapper_registry = registry()
book_table = Table(
"book",
mapper_registry.metadata,
Column("id", Integer, primary_key=True),
Column("title", String(50)),
Column("summary", Text),
Column("cover_image", Blob),
)
class Book:
pass
mapper_registry.map_imperatively(
Book,
book_table,
properties={
"summary": deferred(book_table.c.summary),
"cover_image": deferred(book_table.c.cover_image),
},
)deferred() kann auch anstelle von column_property() verwendet werden, wenn zugeordnete SQL-Ausdrücke verzögert geladen werden sollen.
from sqlalchemy.orm import deferred
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
firstname: Mapped[str] = mapped_column()
lastname: Mapped[str] = mapped_column()
fullname: Mapped[str] = deferred(firstname + " " + lastname)Verwendung von undefer() zum "Eager" Laden von deferrten Spalten¶
Wenn Spalten auf Mappings so konfiguriert sind, dass sie standardmäßig deferrt werden, bewirkt die Option undefer(), dass jede Spalte, die normalerweise deferrt ist, entgruppiert wird, d.h. sie wird zusammen mit allen anderen Spalten des Mappings vorab geladen. Wir können zum Beispiel undefer() auf die Spalte Book.summary anwenden, die im vorherigen Mapping als deferrt angegeben ist.
>>> from sqlalchemy.orm import undefer
>>> book = session.scalar(select(Book).where(Book.id == 2).options(undefer(Book.summary)))
SELECT book.id, book.owner_id, book.title, book.summary
FROM book
WHERE book.id = ?
[...] (2,)
Die Spalte Book.summary wurde nun eager geladen und kann ohne zusätzliche SQL-Emissionen abgerufen werden.
>>> print(book.summary)
another long summaryLaden von deferrten Spalten in Gruppen¶
Normalerweise, wenn eine Spalte mit mapped_column(deferred=True) gemappt wird, wird beim Zugriff auf das deferrte Attribut auf einem Objekt SQL gesendet, um nur diese spezifische Spalte und keine anderen zu laden, selbst wenn das Mapping andere deferrte Spalten hat. In dem häufigen Fall, dass das deferrte Attribut Teil einer Gruppe von Attributen ist, die alle gleichzeitig geladen werden sollen, kann anstatt für jedes Attribut einzeln SQL zu senden, der Parameter mapped_column.deferred_group verwendet werden, der eine beliebige Zeichenkette akzeptiert, die eine gemeinsame Gruppe von Spalten definiert, die entgruppiert werden sollen.
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(
... Text, deferred=True, deferred_group="book_attrs"
... )
... cover_photo: Mapped[bytes] = mapped_column(
... LargeBinary, deferred=True, deferred_group="book_attrs"
... )
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"Mit dem obigen Mapping werden beim Zugriff auf summary oder cover_photo beide Spalten auf einmal mit nur einer SELECT-Anweisung geladen.
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
>>> img_data, summary = book.cover_photo, book.summary
SELECT book.summary AS book_summary, book.cover_photo AS book_cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
Aufheben der Gruppierung mit undefer_group()¶
Wenn deferrte Spalten mit mapped_column.deferred_group wie im vorherigen Abschnitt eingeführt konfiguriert sind, kann die gesamte Gruppe über die Option undefer_group() mit dem Zeichenkettennamen der eager zu ladenden Gruppe vorab geladen werden.
>>> from sqlalchemy.orm import undefer_group
>>> book = session.scalar(
... select(Book).where(Book.id == 2).options(undefer_group("book_attrs"))
... )
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
Sowohl summary als auch cover_photo sind ohne zusätzliche Ladevorgänge verfügbar.
>>> img_data, summary = book.cover_photo, book.summaryAufheben der Gruppierung mit Platzhaltern¶
Die meisten ORM-Ladeoptionen akzeptieren einen Platzhaltersatz, der durch "*" gekennzeichnet ist und angibt, dass die Option auf alle relevanten Attribute angewendet werden soll. Wenn ein Mapping eine Reihe von deferrten Spalten aufweist, können alle diese Spalten auf einmal und ohne Gruppennamen entgruppiert werden, indem ein Platzhalter angegeben wird.
>>> book = session.scalar(select(Book).where(Book.id == 3).options(undefer("*")))
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (3,)
Konfiguration des Mapper-Level-Verhaltens "raiseload"¶
Das Verhalten "raiseload", das zuerst in Verwendung von raiseload zur Verhinderung von deferrten Spaltenladungen eingeführt wurde, kann auch als standardmäßiges Mapper-Level-Verhalten angewendet werden, mit dem Parameter mapped_column.deferred_raiseload von mapped_column(). Bei Verwendung dieses Parameters werden die betroffenen Spalten standardmäßig beim Zugriff fehlschlagen, es sei denn, sie werden explizit mit undefer() oder load_only() zur Abfragezeit "entgruppiert".
>>> class Book(Base):
... __tablename__ = "book"
... id: Mapped[int] = mapped_column(primary_key=True)
... owner_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
... title: Mapped[str]
... summary: Mapped[str] = mapped_column(Text, deferred=True, deferred_raiseload=True)
... cover_photo: Mapped[bytes] = mapped_column(
... LargeBinary, deferred=True, deferred_raiseload=True
... )
...
... def __repr__(self) -> str:
... return f"Book(id={self.id!r}, title={self.title!r})"Mit dem obigen Mapping sind die Spalten .summary und .cover_photo standardmäßig nicht ladbar.
>>> book = session.scalar(select(Book).where(Book.id == 2))
SELECT book.id, book.owner_id, book.title
FROM book
WHERE book.id = ?
[...] (2,)
>>> book.summary
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: 'Book.summary' is not available due to raiseload=TrueNur durch Überschreiben ihres Verhaltens zur Abfragezeit, typischerweise mit undefer() oder undefer_group(), oder seltener mit defer(), können die Attribute geladen werden. Das folgende Beispiel wendet undefer('*') an, um alle Attribute zu entgruppieren, und verwendet außerdem Populate Existing, um die bereits geladenen Loader-Optionen des Objekts zu aktualisieren.
>>> book = session.scalar(
... select(Book)
... .where(Book.id == 2)
... .options(undefer("*"))
... .execution_options(populate_existing=True)
... )
SELECT book.id, book.owner_id, book.title, book.summary, book.cover_photo
FROM book
WHERE book.id = ?
[...] (2,)
>>> book.summary
'another long summary'Laden beliebiger SQL-Ausdrücke in Objekte¶
Wie in Auswahl von ORM-Entitäten und Attributen und anderswo diskutiert, kann die Konstruktion select() verwendet werden, um beliebige SQL-Ausdrücke in einem Ergebnissatz zu laden. So könnten wir beispielsweise eine Abfrage ausgeben, die User-Objekte lädt, aber auch eine Anzahl, wie viele Bücher jeder User besaß, beinhaltet. Wir könnten func.count(Book.id) verwenden, um eine "count"-Spalte zu einer Abfrage hinzuzufügen, die einen JOIN zu Book sowie eine GROUP BY owner id enthält. Dies liefert Row-Objekte, die jeweils zwei Einträge enthalten, einen für User und einen für func.count(Book.id).
>>> from sqlalchemy import func
>>> stmt = select(User, func.count(Book.id)).join_from(User, Book).group_by(Book.owner_id)
>>> for user, book_count in session.execute(stmt):
... print(f"Username: {user.name} Number of books: {book_count}")
SELECT user_account.id, user_account.name, user_account.fullname,
count(book.id) AS count_1
FROM user_account JOIN book ON user_account.id = book.owner_id
GROUP BY book.owner_id
[...] ()
Username: spongebob Number of books: 3
Username: sandy Number of books: 3Im obigen Beispiel werden die Entität User und der SQL-Ausdruck "book count" getrennt zurückgegeben. Ein beliebter Anwendungsfall ist jedoch die Erzeugung einer Abfrage, die nur User-Objekte liefert, die beispielsweise mit Session.scalars() durchlaufen werden können, wobei das Ergebnis des SQL-Ausdrucks func.count(Book.id) *dynamisch* auf jede User-Entität angewendet wird. Das Endergebnis wäre ähnlich wie in dem Fall, dass ein beliebiger SQL-Ausdruck über column_property() der Klasse zugeordnet wurde, mit dem Unterschied, dass der SQL-Ausdruck zur Abfragezeit geändert werden kann. Für diesen Anwendungsfall bietet SQLAlchemy die Ladeoption with_expression(), die in Kombination mit der Mapper-Level-Direktive query_expression() dieses Ergebnis erzielen kann.
Um with_expression() auf eine Abfrage anzuwenden, muss die zugeordnete Klasse ein ORM-zugeordnetes Attribut über die query_expression()-Direktive vorkonfiguriert haben; diese Direktive erzeugt ein Attribut auf der zugeordneten Klasse, das für den Empfang von Abfragezeit-SQL-Ausdrücken geeignet ist. Unten fügen wir der Klasse User ein neues Attribut User.book_count hinzu. Dieses ORM-zugeordnete Attribut ist schreibgeschützt und hat keinen Standardwert; der Zugriff darauf auf einer geladenen Instanz ergibt normalerweise None.
>>> from sqlalchemy.orm import query_expression
>>> class User(Base):
... __tablename__ = "user_account"
... id: Mapped[int] = mapped_column(primary_key=True)
... name: Mapped[str]
... fullname: Mapped[Optional[str]]
... book_count: Mapped[int] = query_expression()
...
... def __repr__(self) -> str:
... return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"Mit dem in unserem Mapping konfigurierten Attribut User.book_count können wir es mit Daten aus einem SQL-Ausdruck über die Ladeoption with_expression() mit Daten aus einem SQL-Ausdruck befüllen, um jedem User-Objekt beim Laden einen benutzerdefinierten SQL-Ausdruck zuzuweisen.
>>> from sqlalchemy.orm import with_expression
>>> stmt = (
... select(User)
... .join_from(User, Book)
... .group_by(Book.owner_id)
... .options(with_expression(User.book_count, func.count(Book.id)))
... )
>>> for user in session.scalars(stmt):
... print(f"Username: {user.name} Number of books: {user.book_count}")
SELECT count(book.id) AS count_1, user_account.id, user_account.name,
user_account.fullname
FROM user_account JOIN book ON user_account.id = book.owner_id
GROUP BY book.owner_id
[...] ()
Username: spongebob Number of books: 3
Username: sandy Number of books: 3Oben haben wir unseren Ausdruck func.count(Book.id) aus dem Spaltenargument des Konstrukts select() in die Ladeoption with_expression() verschoben. Das ORM betrachtet dies dann als eine spezielle Spaltenladeoption, die dynamisch auf die Anweisung angewendet wird.
Das Mapping query_expression() hat diese Einschränkungen.
Auf einem Objekt, bei dem
with_expression()nicht zum Befüllen des Attributs verwendet wurde, hat das Attribut einer Objektinstanz den WertNone, es sei denn, auf dem Mapping wurde der Parameterquery_expression.default_exprauf einen Standard-SQL-Ausdruck gesetzt.Der Wert von
with_expression()wird nicht auf ein bereits geladenes Objekt angewendet, es sei denn, Populate Existing wird verwendet. Das folgende Beispiel funktioniert nicht, da das ObjektAbereits geladen ist.# load the first A obj = session.scalars(select(A).order_by(A.id)).first() # load the same A with an option; expression will **not** be applied # to the already-loaded object obj = session.scalars(select(A).options(with_expression(A.expr, some_expr))).first()
Um sicherzustellen, dass das Attribut auf einem bestehenden Objekt neu geladen wird, verwenden Sie die Ausführungsoption Populate Existing, um sicherzustellen, dass alle Spalten neu befüllt werden.
obj = session.scalars( select(A) .options(with_expression(A.expr, some_expr)) .execution_options(populate_existing=True) ).first()
Der SQL-Ausdruck
with_expression()geht verloren, wenn das Objekt abgelaufen ist. Sobald das Objekt abgelaufen ist, entweder überSession.expire()oder über das expire_on_commit-Verhalten vonSession.commit(), ist der SQL-Ausdruck und sein Wert nicht mehr mit dem Attribut verbunden und gibt bei nachfolgendem ZugriffNonezurück.with_expression()wirkt als Ladeoption nur auf den äußersten Teil einer Abfrage und nur für eine Abfrage gegen eine vollständige Entität, nicht für beliebige Spaltenauswahlen, innerhalb von Unterabfragen oder Elemente einer zusammengesetzten Anweisung wie UNION. Siehe den nächsten Abschnitt Verwendung von with_expression() mit UNIONs und anderen Unterabfragen für ein Beispiel.Das gemappte Attribut kann nicht auf andere Teile der Abfrage, wie die WHERE-Klausel, die ORDER BY-Klausel angewendet werden und den Ad-hoc-Ausdruck verwenden; das heißt, dies funktioniert nicht
# can't refer to A.expr elsewhere in the query stmt = ( select(A) .options(with_expression(A.expr, A.x + A.y)) .filter(A.expr > 5) .order_by(A.expr) )
Der Ausdruck
A.exprwird in der obigen WHERE- und ORDER BY-Klausel zu NULL aufgelöst. Um den Ausdruck in der gesamten Abfrage zu verwenden, weisen Sie ihn einer Variablen zu und verwenden Sie diese# assign desired expression up front, then refer to that in # the query a_expr = A.x + A.y stmt = ( select(A) .options(with_expression(A.expr, a_expr)) .filter(a_expr > 5) .order_by(a_expr) )
Siehe auch
Die Option with_expression() ist eine spezielle Option, die verwendet wird, um SQL-Ausdrücke zur Abfragezeit dynamisch auf gemappte Klassen anzuwenden. Für gewöhnliche feste SQL-Ausdrücke, die auf Mappern konfiguriert sind, siehe den Abschnitt SQL-Ausdrücke als gemappte Attribute.
Verwendung von with_expression() mit UNIONs und anderen Unterabfragen¶
Das Konstrukt with_expression() ist eine ORM-Ladeoption und kann daher nur auf die äußerste Ebene einer SELECT-Anweisung angewendet werden, die eine bestimmte ORM-Entität laden soll. Es hat keine Auswirkung, wenn es innerhalb einer select() verwendet wird, die dann als Unterabfrage oder als Element einer zusammengesetzten Anweisung wie UNION verwendet wird.
Um beliebige SQL-Ausdrücke in Unterabfragen zu verwenden, sollten normale Core-Methoden zum Hinzufügen von Ausdrücken verwendet werden. Um einen aus einer Unterabfrage abgeleiteten Ausdruck auf die query_expression()-Attribute der ORM-Entität abzubilden, wird with_expression() auf der obersten Ebene des ORM-Objektsladens verwendet und verweist auf den SQL-Ausdruck innerhalb der Unterabfrage.
Im folgenden Beispiel werden zwei select()-Konstrukte gegen die ORM-Entität A mit einem zusätzlichen SQL-Ausdruck namens expr verwendet und mit union_all() kombiniert. Dann wird auf der obersten Ebene die Entität A aus diesem UNION ausgewählt, unter Verwendung der unter Auswählen von Entitäten aus UNIONs und anderen Mengenoperationen beschriebenen Abfragetechnik, und eine Option mit with_expression() hinzugefügt, um diesen SQL-Ausdruck auf neu geladene Instanzen von A zu extrahieren.
>>> from sqlalchemy import union_all
>>> s1 = (
... select(User, func.count(Book.id).label("book_count"))
... .join_from(User, Book)
... .where(User.name == "spongebob")
... )
>>> s2 = (
... select(User, func.count(Book.id).label("book_count"))
... .join_from(User, Book)
... .where(User.name == "sandy")
... )
>>> union_stmt = union_all(s1, s2)
>>> orm_stmt = (
... select(User)
... .from_statement(union_stmt)
... .options(with_expression(User.book_count, union_stmt.selected_columns.book_count))
... )
>>> for user in session.scalars(orm_stmt):
... print(f"Username: {user.name} Number of books: {user.book_count}")
SELECT user_account.id, user_account.name, user_account.fullname, count(book.id) AS book_count
FROM user_account JOIN book ON user_account.id = book.owner_id
WHERE user_account.name = ?
UNION ALL
SELECT user_account.id, user_account.name, user_account.fullname, count(book.id) AS book_count
FROM user_account JOIN book ON user_account.id = book.owner_id
WHERE user_account.name = ?
[...] ('spongebob', 'sandy')
Username: spongebob Number of books: 3
Username: sandy Number of books: 3API für Spaltenladen¶
| Objektname | Beschreibung |
|---|---|
defer(key, *addl_attrs, [raiseload]) |
Gibt an, dass das gegebene spaltenorientierte Attribut verzögert werden soll, d. h. erst geladen, wenn darauf zugegriffen wird. |
deferred(column, *additional_columns, [group, raiseload, comparator_factory, init, repr, default, default_factory, compare, kw_only, hash, active_history, expire_on_flush, info, doc]) |
Gibt ein spaltenbasiertes gemapptes Attribut an, das standardmäßig erst geladen wird, wenn darauf zugegriffen wird. |
load_only(*attrs, [raiseload]) |
Gibt an, dass für eine bestimmte Entität nur die angegebenen spaltenbasierten Attributnamen geladen werden sollen; alle anderen werden verzögert. |
query_expression([default_expr], *, [repr, compare, expire_on_flush, info, doc]) |
Gibt ein Attribut an, das aus einem SQL-Ausdruck zur Abfragezeit befüllt wird. |
undefer(key, *addl_attrs) |
Gibt an, dass das gegebene spaltenorientierte Attribut nicht verzögert werden soll, d. h. in der SELECT-Anweisung der gesamten Entität angegeben werden soll. |
undefer_group(name) |
Gibt an, dass Spalten innerhalb des gegebenen Gruppennamens für die Verzögerung nicht verzögert werden sollen. |
with_expression(key, expression) |
Wendet einen Ad-hoc-SQL-Ausdruck auf ein „deferred expression“-Attribut an. |
- function sqlalchemy.orm.defer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) → _AbstractLoad¶
Gibt an, dass das gegebene spaltenorientierte Attribut verzögert werden soll, d. h. erst geladen, wenn darauf zugegriffen wird.
Diese Funktion ist Teil der
Load-Schnittstelle und unterstützt sowohl die verkettete als auch die eigenständige Operation.z. B.
from sqlalchemy.orm import defer session.query(MyClass).options( defer(MyClass.attribute_one), defer(MyClass.attribute_two) )
Um eine verzögerte Ladung eines Attributs einer zugehörigen Klasse anzugeben, kann der Pfad Token für Token angegeben werden, wobei der Ladestil für jede Verknüpfung in der Kette spezifiziert wird. Um den Ladestil für eine Verknüpfung unverändert zu lassen, verwenden Sie
defaultload()session.query(MyClass).options( defaultload(MyClass.someattr).defer(RelatedClass.some_column) )
Mehrere auf Beziehungen bezogene Verzögerungsoptionen können mit
Load.options()gebündelt werden.select(MyClass).options( defaultload(MyClass.someattr).options( defer(RelatedClass.some_column), defer(RelatedClass.some_other_column), defer(RelatedClass.another_column), ) )
- Parameter:
key¶ – Zu verzögerndes Attribut.
raiseload¶ – löst
InvalidRequestErroraus, anstatt einen Wert verzögert zu laden, wenn auf das verzögerte Attribut zugegriffen wird. Wird verwendet, um die Ausgabe von unerwünschtem SQL zu verhindern.
Neu in Version 1.4.
- function sqlalchemy.orm.deferred(column: _ORMColumnExprArgument[_T], *additional_columns: _ORMColumnExprArgument[Any], group: str | None = None, raiseload: bool = False, comparator_factory: Type[PropComparator[_T]] | None = None, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: Any | None = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, active_history: bool = False, expire_on_flush: bool = True, info: _InfoType | None = None, doc: str | None = None) → MappedSQLExpression[_T]¶
Gibt ein spaltenbasiertes gemapptes Attribut an, das standardmäßig erst geladen wird, wenn darauf zugegriffen wird.
Bei der Verwendung von
mapped_column()wird die gleiche Funktionalität wie beimdeferred()-Konstrukt durch die Verwendung des Parametersmapped_column.deferredbereitgestellt.- Parameter:
*columns¶ – Spalten, die gemappt werden sollen. Dies ist typischerweise ein einzelnes
Column-Objekt, aber eine Sammlung wird unterstützt, um mehrere Spalten zu unterstützen, die unter demselben Attribut gemappt sind.raiseload¶ –
boolean, wenn True, gibt an, dass eine Ausnahme ausgelöst werden soll, wenn die Ladeoperation stattfinden soll.
Neu in Version 1.4.
Zusätzliche Argumente sind die gleichen wie bei
column_property().
- function sqlalchemy.orm.query_expression(default_expr: _ORMColumnExprArgument[_T] = <sqlalchemy.sql.elements.Null object>, *, repr: Union[_NoArg, bool] = _NoArg.NO_ARG, compare: Union[_NoArg, bool] = _NoArg.NO_ARG, expire_on_flush: bool = True, info: Optional[_InfoType] = None, doc: Optional[str] = None) → MappedSQLExpression[_T]¶
Gibt ein Attribut an, das aus einem SQL-Ausdruck zur Abfragezeit befüllt wird.
- Parameter:
default_expr¶ – Optionaler SQL-Ausdruck, der in allen Fällen verwendet wird, wenn er nicht später mit
with_expression()zugewiesen wird.
Neu seit Version 1.2.
Siehe auch
Laden von beliebigen SQL-Ausdrücken in Objekte - Hintergrund und Anwendungsbeispiele
- function sqlalchemy.orm.load_only(*attrs: Literal['*'] | QueryableAttribute[Any], raiseload: bool = False) → _AbstractLoad¶
Gibt an, dass für eine bestimmte Entität nur die angegebenen spaltenbasierten Attributnamen geladen werden sollen; alle anderen werden verzögert.
Diese Funktion ist Teil der
Load-Schnittstelle und unterstützt sowohl die verkettete als auch die eigenständige Operation.Beispiel - Angenommen, eine Klasse
User, laden Sie nur die Attributenameundfullname.session.query(User).options(load_only(User.name, User.fullname))Beispiel - Angenommen, eine Beziehung
User.addresses -> Address, geben Sie Subquery-Laden für die SammlungUser.addressesan, aber laden Sie bei jedemAddress-Objekt nur das Attributemail_address.session.query(User).options( subqueryload(User.addresses).load_only(Address.email_address) )
Für eine Anweisung mit mehreren Entitäten kann die führende Entität mit dem
Load-Konstruktor gezielt angesprochen werden.stmt = ( select(User, Address) .join(User.addresses) .options( Load(User).load_only(User.name, User.fullname), Load(Address).load_only(Address.email_address), ) )
Wenn diese Option zusammen mit der Ausführungsoption populate_existing verwendet wird, werden nur die angegebenen Attribute aktualisiert.
- Parameter:
*attrs¶ – Zu ladende Attribute, alle anderen werden verzögert.
raiseload¶ –
löst
InvalidRequestErroraus, anstatt einen Wert verzögert zu laden, wenn auf ein verzögertes Attribut zugegriffen wird. Wird verwendet, um die Ausgabe von unerwünschtem SQL zu verhindern.Neu in Version 2.0.
- Parameter:
*attrs¶ – Zu ladende Attribute, alle anderen werden verzögert.
raiseload¶ –
löst
InvalidRequestErroraus, anstatt einen Wert verzögert zu laden, wenn auf ein verzögertes Attribut zugegriffen wird. Wird verwendet, um die Ausgabe von unerwünschtem SQL zu verhindern.Neu in Version 2.0.
- function sqlalchemy.orm.undefer(key: Literal['*'] | QueryableAttribute[Any], *addl_attrs: Literal['*'] | QueryableAttribute[Any]) → _AbstractLoad¶
Gibt an, dass das gegebene spaltenorientierte Attribut nicht verzögert werden soll, d. h. in der SELECT-Anweisung der gesamten Entität angegeben werden soll.
Die nicht verzögerte Spalte ist typischerweise auf der Zuordnung als
deferred()-Attribut eingerichtet.Diese Funktion ist Teil der
Load-Schnittstelle und unterstützt sowohl die verkettete als auch die eigenständige Operation.Beispiele
# undefer two columns session.query(MyClass).options( undefer(MyClass.col1), undefer(MyClass.col2) ) # undefer all columns specific to a single class using Load + * session.query(MyClass, MyOtherClass).options(Load(MyClass).undefer("*")) # undefer a column on a related object select(MyClass).options(defaultload(MyClass.items).undefer(MyClass.text))
- Parameter:
key¶ – Nicht zu verzögerndes Attribut.
- function sqlalchemy.orm.undefer_group(name: str) → _AbstractLoad¶
Gibt an, dass Spalten innerhalb des gegebenen Gruppennamens für die Verzögerung nicht verzögert werden sollen.
Die nicht verzögerten Spalten sind auf der Zuordnung als
deferred()-Attribute eingerichtet und enthalten einen „Gruppen“-Namen.Z.B.
session.query(MyClass).options(undefer_group("large_attrs"))Um eine Gruppe von Attributen einer zugehörigen Entität nicht zu verzögern, kann der Pfad über Beziehungs-Ladeoptionen wie
defaultload()angegeben werden.select(MyClass).options( defaultload("someattr").undefer_group("large_attrs") )
- function sqlalchemy.orm.with_expression(key: _AttrType, expression: _ColumnExpressionArgument[Any]) → _AbstractLoad¶
Wendet einen Ad-hoc-SQL-Ausdruck auf ein „deferred expression“-Attribut an.
Diese Option wird in Verbindung mit dem Mapper-Level-Konstrukt
query_expression()verwendet, das ein Attribut angibt, das das Ziel eines Ad-hoc-SQL-Ausdrucks sein soll.Z. B.
stmt = select(SomeClass).options( with_expression(SomeClass.x_y_expr, SomeClass.x + SomeClass.y) )
Neu seit Version 1.2.
- Parameter:
Siehe auch
Laden von beliebigen SQL-Ausdrücken in Objekte - Hintergrund und Anwendungsbeispiele
Die Designs von flambé! dem Drachen und Der Alchemist wurden von Rotem Yaari erstellt und großzügig gespendet.
Erstellt mit Sphinx 7.2.6. Dokumentation zuletzt generiert: Di 11 Mär 2025 14:40:17 EDT