SQLAlchemy 2.0 Dokumentation
SQLAlchemy ORM
- ORM Schnellstart
- ORM Abgebildete Klassenkonfiguration
- Beziehungskonfiguration
- ORM Abfragehandbuch
- Verwendung der Sitzung
- Grundlagen der Session
- Zustandsverwaltung
- Kaskaden
- Transaktionen und Verbindungsverwaltung
- Zusätzliche Persistenztechniken
- Kontextuelle/Thread-lokale Sessions
- Tracking queries, object and Session Changes with Events¶
- Session API
- Ereignisse und Interna
- ORM Erweiterungen
- ORM Beispiele
Projektversionen
Tracking queries, object and Session Changes with Events¶
SQLAlchemy bietet ein umfangreiches Event Listening System, das sowohl im Core als auch im ORM verwendet wird. Innerhalb des ORM gibt es eine Vielzahl von Event-Listener-Hooks, die auf API-Ebene unter ORM Events dokumentiert sind. Diese Sammlung von Events ist im Laufe der Jahre gewachsen und umfasst viele sehr nützliche neue Events sowie einige ältere Events, die nicht mehr so relevant sind wie früher. Dieser Abschnitt versucht, die wichtigsten Event-Hooks vorzustellen und zu erläutern, wann sie verwendet werden könnten.
Execute Events¶
Neu in Version 1.4: Die Session bietet nun einen einzigen, umfassenden Hook, der dazu dient, alle SELECT-Anweisungen, die im Auftrag des ORM ausgeführt werden, sowie Massen-UPDATE- und DELETE-Anweisungen abzufangen. Dieser Hook ersetzt sowohl das frühere Event QueryEvents.before_compile() als auch QueryEvents.before_compile_update() und QueryEvents.before_compile_delete().
Session verfügt über ein umfassendes System, mit dem alle über die Methode Session.execute() aufgerufenen Abfragen abgefangen und modifiziert werden können. Dies schließt alle SELECT-Anweisungen ein, die vom Query ausgegeben werden, sowie alle SELECT-Anweisungen, die im Auftrag von Spalten- und Beziehungs-Loadern ausgegeben werden. Das System nutzt den Event-Hook SessionEvents.do_orm_execute() sowie das Objekt ORMExecuteState, um den Event-Status darzustellen.
Basic Query Interception¶
SessionEvents.do_orm_execute() ist in erster Linie nützlich für jede Art von Abfrage-Abfangung, einschließlich derjenigen, die von Query mit 1.x style ausgegeben werden, sowie wenn ein ORM-aktivierter 2.0 style select()-, update()- oder delete()-Konstrukt an Session.execute() übergeben wird. Das Konstrukt ORMExecuteState bietet Accessoren, die Modifikationen an Anweisungen, Parametern und Optionen ermöglichen.
Session = sessionmaker(engine)
@event.listens_for(Session, "do_orm_execute")
def _do_orm_execute(orm_execute_state):
if orm_execute_state.is_select:
# add populate_existing for all SELECT statements
orm_execute_state.update_execution_options(populate_existing=True)
# check if the SELECT is against a certain entity and add an
# ORDER BY if so
col_descriptions = orm_execute_state.statement.column_descriptions
if col_descriptions[0]["entity"] is MyEntity:
orm_execute_state.statement = statement.order_by(MyEntity.name)Das obige Beispiel zeigt einige einfache Modifikationen an SELECT-Anweisungen. Auf dieser Ebene soll der Event-Hook SessionEvents.do_orm_execute() die frühere Verwendung des Events QueryEvents.before_compile() ersetzen, das nicht konsistent für verschiedene Arten von Loadern ausgelöst wurde; zusätzlich gilt QueryEvents.before_compile() nur für die Verwendung im 1.x style mit Query und nicht für die Verwendung im 2.0 style von Session.execute().
Adding global WHERE / ON criteria¶
Eine der am häufigsten nachgefragten Funktionen zur Abfrageerweiterung ist die Möglichkeit, allen Vorkommen einer Entität in allen Abfragen WHERE-Kriterien hinzuzufügen. Dies kann durch die Verwendung der Abfrageoption with_loader_criteria() erreicht werden, die allein verwendet werden kann oder idealerweise innerhalb des Events SessionEvents.do_orm_execute() verwendet wird.
from sqlalchemy.orm import with_loader_criteria
Session = sessionmaker(engine)
@event.listens_for(Session, "do_orm_execute")
def _do_orm_execute(orm_execute_state):
if (
orm_execute_state.is_select
and not orm_execute_state.is_column_load
and not orm_execute_state.is_relationship_load
):
orm_execute_state.statement = orm_execute_state.statement.options(
with_loader_criteria(MyEntity.public == True)
)Oben wird eine Option zu allen SELECT-Anweisungen hinzugefügt, die alle Abfragen gegen MyEntity darauf beschränkt, nach public == True zu filtern. Die Kriterien werden auf alle Ladevorgänge dieser Klasse innerhalb des Geltungsbereichs der unmittelbaren Abfrage angewendet. Die Option with_loader_criteria() breitet sich standardmäßig auch automatisch auf Beziehungs-Loader aus, die auf nachfolgende Beziehungs-Ladevorgänge angewendet werden, einschließlich Lazy Loads, Selectin Loads usw.
Für eine Reihe von Klassen, die alle eine gemeinsame Spaltenstruktur aufweisen und mit einem deklarativen Mixin aufgebaut sind, kann die Mixin-Klasse selbst in Verbindung mit der Option with_loader_criteria() verwendet werden, indem ein Python-Lambda genutzt wird. Das Python-Lambda wird zur Abfragekompilierungszeit gegen die spezifischen Entitäten aufgerufen, die den Kriterien entsprechen. Gegeben sei eine Reihe von Klassen, die auf einem Mixin namens HasTimestamp basieren.
import datetime
class HasTimestamp:
timestamp = mapped_column(DateTime, default=datetime.datetime.now)
class SomeEntity(HasTimestamp, Base):
__tablename__ = "some_entity"
id = mapped_column(Integer, primary_key=True)
class SomeOtherEntity(HasTimestamp, Base):
__tablename__ = "some_entity"
id = mapped_column(Integer, primary_key=True)Die obigen Klassen SomeEntity und SomeOtherEntity haben jeweils eine Spalte timestamp, die standardmäßig auf das aktuelle Datum und die aktuelle Uhrzeit gesetzt ist. Ein Event kann verwendet werden, um alle Objekte abzufangen, die von HasTimestamp abgeleitet sind, und ihre timestamp-Spalte auf ein Datum zu filtern, das nicht älter als einen Monat ist.
@event.listens_for(Session, "do_orm_execute")
def _do_orm_execute(orm_execute_state):
if (
orm_execute_state.is_select
and not orm_execute_state.is_column_load
and not orm_execute_state.is_relationship_load
):
one_month_ago = datetime.datetime.today() - datetime.timedelta(months=1)
orm_execute_state.statement = orm_execute_state.statement.options(
with_loader_criteria(
HasTimestamp,
lambda cls: cls.timestamp >= one_month_ago,
include_aliases=True,
)
)Warnung
Die Verwendung eines Lambdas innerhalb des Aufrufs von with_loader_criteria() wird nur einmal pro eindeutiger Klasse aufgerufen. Benutzerdefinierte Funktionen sollten nicht innerhalb dieses Lambdas aufgerufen werden. Siehe Verwendung von Lambdas zur erheblichen Beschleunigung der Statement-Erstellung für einen Überblick über das Feature "Lambda SQL", das nur für fortgeschrittene Benutzer gedacht ist.
Siehe auch
ORM Query Events - enthält funktionierende Beispiele der oben genannten with_loader_criteria()-Rezepte.
Re-Executing Statements¶
Deep Alchemy
Die Funktion zur Wiederholung der Ausführung von Anweisungen beinhaltet eine etwas komplizierte rekursive Sequenz und ist dazu gedacht, das ziemlich schwierige Problem zu lösen, die Ausführung einer SQL-Anweisung auf verschiedene Nicht-SQL-Kontexte umzuleiten. Die beiden unten verlinkten Beispiele "dogpile caching" und "horizontal sharding" sollen als Leitfaden dienen, wann diese eher fortgeschrittene Funktion angemessen ist.
Das Objekt ORMExecuteState ist in der Lage, die Ausführung der gegebenen Anweisung zu steuern; dazu gehört die Möglichkeit, die Anweisung gar nicht erst auszuführen und stattdessen ein vordefiniertes Ergebnis-Set aus einem Cache zurückzugeben, sowie die Möglichkeit, dieselbe Anweisung wiederholt mit unterschiedlichem Zustand auszuführen, z. B. sie gegen mehrere Datenbankverbindungen auszuführen und die Ergebnisse dann im Speicher zusammenzuführen. Beide dieser fortgeschrittenen Muster werden in der Beispielsuite von SQLAlchemy wie unten detailliert gezeigt.
Innerhalb des Event-Hooks SessionEvents.do_orm_execute() kann die Methode ORMExecuteState.invoke_statement() verwendet werden, um die Anweisung mit einer neuen, verschachtelten Ausführung von Session.execute() auszuführen. Dies unterbricht die nachfolgende Behandlung der aktuellen Ausführung und gibt stattdessen das von der inneren Ausführung zurückgegebene Result-Objekt zurück. Die bisher für den Hook SessionEvents.do_orm_execute() aufgerufenen Event-Handler werden bei diesem verschachtelten Aufruf ebenfalls übersprungen.
Die Methode ORMExecuteState.invoke_statement() gibt ein Result-Objekt zurück. Dieses Objekt kann dann in ein cachefähiges Format "eingefroren" und wieder in ein neues Result-Objekt "aufgetaut" werden, und seine Daten können mit denen anderer Result-Objekte zusammengeführt werden.
Z. B. Verwendung von SessionEvents.do_orm_execute() zur Implementierung eines Caches
from sqlalchemy.orm import loading
cache = {}
@event.listens_for(Session, "do_orm_execute")
def _do_orm_execute(orm_execute_state):
if "my_cache_key" in orm_execute_state.execution_options:
cache_key = orm_execute_state.execution_options["my_cache_key"]
if cache_key in cache:
frozen_result = cache[cache_key]
else:
frozen_result = orm_execute_state.invoke_statement().freeze()
cache[cache_key] = frozen_result
return loading.merge_frozen_result(
orm_execute_state.session,
orm_execute_state.statement,
frozen_result,
load=False,
)Mit dem obigen Hook würde ein Beispiel für die Verwendung des Caches wie folgt aussehen.
stmt = (
select(User).where(User.name == "sandy").execution_options(my_cache_key="key_sandy")
)
result = session.execute(stmt)Oben wird eine benutzerdefinierte Ausführungsoption an Select.execution_options() übergeben, um einen "Cache-Schlüssel" zu etablieren, der dann vom Hook SessionEvents.do_orm_execute() abgefangen wird. Dieser Cache-Schlüssel wird dann mit einem FrozenResult-Objekt abgeglichen, das sich im Cache befinden kann, und wenn es vorhanden ist, wird das Objekt wiederverwendet. Das Rezept verwendet die Methode Result.freeze(), um ein Result-Objekt zu "einfrieren", das dann ORM-Ergebnisse enthält, so dass es in einem Cache gespeichert und mehrfach verwendet werden kann. Um ein Live-Ergebnis aus dem "eingefrorenen" Ergebnis zurückzugeben, wird die Funktion merge_frozen_result() verwendet, um die "eingefrorenen" Daten aus dem Ergebnisobjekt in die aktuelle Session zusammenzuführen.
Das obige Beispiel ist als vollständiges Beispiel unter Dogpile Caching implementiert.
Die Methode ORMExecuteState.invoke_statement() kann auch mehrmals aufgerufen werden, wobei unterschiedliche Informationen an den Parameter ORMExecuteState.invoke_statement.bind_arguments übergeben werden, so dass die Session jedes Mal unterschiedliche Engine-Objekte verwendet. Dies gibt jedes Mal ein anderes Result-Objekt zurück; diese Ergebnisse können mit der Methode Result.merge() zusammengeführt werden. Dies ist die Technik, die von der Erweiterung Horizontal Sharding verwendet wird; siehe den Quellcode zur Vertrautmachung.
Persistence Events¶
Wahrscheinlich die am weitesten verbreitete Reihe von Events sind die "Persistence Events", die dem Flush-Prozess entsprechen. Beim Flush werden alle Entscheidungen über ausstehende Änderungen an Objekten getroffen und dann in Form von INSERT-, UPDATE- und DELETE-Anweisungen an die Datenbank gesendet.
before_flush()¶
Der Hook SessionEvents.before_flush() ist bei weitem das allgemein nützlichste Event, das verwendet wird, wenn eine Anwendung sicherstellen möchte, dass zusätzliche Persistenzänderungen an die Datenbank vorgenommen werden, wenn ein Flush durchgeführt wird. Verwenden Sie SessionEvents.before_flush(), um Objekte zu bearbeiten, ihren Zustand zu validieren und zusätzliche Objekte und Referenzen zusammenzustellen, bevor sie persistiert werden. Innerhalb dieses Events ist es sicher, den Zustand der Session zu manipulieren, d.h. neue Objekte können an sie angehängt, Objekte gelöscht und einzelne Attribute von Objekten frei geändert werden, und diese Änderungen werden in den Flush-Prozess einbezogen, wenn der Event-Hook abgeschlossen ist.
Der typische Hook SessionEvents.before_flush() wird die Sammlungen Session.new, Session.dirty und Session.deleted durchsuchen, um nach Objekten zu suchen, bei denen etwas passieren wird.
Für Illustrationen von SessionEvents.before_flush() siehe Beispiele wie Versioning with a History Table und Versioning using Temporal Rows.
after_flush()¶
Der Hook SessionEvents.after_flush() wird aufgerufen, nachdem die SQL-Anweisungen für einen Flush-Prozess ausgegeben wurden, aber bevor der Zustand der geflushten Objekte geändert wurde. Das heißt, Sie können immer noch die Sammlungen Session.new, Session.dirty und Session.deleted inspizieren, um zu sehen, was gerade geflusht wurde, und Sie können auch Historienverfolgungsfunktionen wie die von AttributeState bereitgestellten verwenden, um zu sehen, welche Änderungen gerade persistiert wurden. Im Event SessionEvents.after_flush() kann zusätzliche SQL an die Datenbank gesendet werden, basierend auf den beobachteten Änderungen.
after_flush_postexec()¶
SessionEvents.after_flush_postexec() wird kurz nach SessionEvents.after_flush() aufgerufen, aber nachdem der Zustand der Objekte geändert wurde, um dem gerade stattgefundenen Flush Rechnung zu tragen. Die Sammlungen Session.new, Session.dirty und Session.deleted sind hier normalerweise vollständig leer. Verwenden Sie SessionEvents.after_flush_postexec(), um die Identity Map nach finalisierten Objekten zu inspizieren und möglicherweise zusätzliche SQL-Befehle auszugeben. In diesem Hook besteht die Möglichkeit, neue Änderungen an Objekten vorzunehmen, was bedeutet, dass die Session wieder in einen "schmutzigen" Zustand gerät; die Mechanik der Session führt dazu, dass sie erneut flusht, wenn neue Änderungen in diesem Hook erkannt werden, falls der Flush im Kontext von Session.commit() aufgerufen wurde; andernfalls werden die ausstehenden Änderungen als Teil des nächsten normalen Flushes gebündelt. Wenn der Hook neue Änderungen innerhalb eines Session.commit() erkennt, stellt ein Zähler sicher, dass eine Endlosschleife in dieser Hinsicht nach 100 Iterationen gestoppt wird, falls ein Hook SessionEvents.after_flush_postexec() wiederholt neue, zu flushende Zustände hinzufügt, jedes Mal, wenn er aufgerufen wird.
Mapper-level Flush Events¶
Zusätzlich zu den Flush-Level-Hooks gibt es auch eine Reihe von Hooks, die feingranularer sind, d.h. sie werden pro Objekt aufgerufen und sind nach INSERT, UPDATE oder DELETE innerhalb des Flush-Prozesses unterteilt. Dies sind die Mapper-Persistenz-Hooks, und sie sind ebenfalls sehr beliebt. Diese Events müssen jedoch mit mehr Vorsicht angegangen werden, da sie im Kontext des bereits laufenden Flush-Prozesses ablaufen; viele Operationen sind hier nicht sicher durchzuführen.
Die Events sind
Hinweis
Es ist wichtig zu beachten, dass diese Events nur für den Session Flush Operation gelten und nicht für die ORM-Level INSERT/UPDATE/DELETE-Funktionalität, die unter ORM-Enabled INSERT, UPDATE, and DELETE statements beschrieben wird. Um ORM-Level DML abzufangen, verwenden Sie das Event SessionEvents.do_orm_execute().
Jedes Event erhält den Mapper, das gemappte Objekt selbst und die Connection, die verwendet wird, um eine INSERT-, UPDATE- oder DELETE-Anweisung auszugeben. Der Reiz dieser Events liegt auf der Hand: Wenn eine Anwendung eine bestimmte Aktivität mit dem Zeitpunkt verknüpfen möchte, an dem ein bestimmter Objekttyp mit einem INSERT persistiert wird, ist der Hook sehr spezifisch; im Gegensatz zum Event SessionEvents.before_flush() muss nicht durch Sammlungen wie Session.new gesucht werden, um Ziele zu finden. Allerdings wurde der Flush-Plan, der die vollständige Liste aller zu sendenden INSERT-, UPDATE-, DELETE-Anweisungen darstellt, bereits entschieden, wenn diese Events aufgerufen werden, und zu diesem Zeitpunkt können keine Änderungen mehr vorgenommen werden. Daher sind die einzigen Änderungen, die an den gegebenen Objekten überhaupt möglich sind, Attribute, die lokal zur Zeile des Objekts sind. Jede andere Änderung am Objekt oder an anderen Objekten wirkt sich auf den Zustand der Session aus, die dann nicht mehr richtig funktioniert.
Zu den Operationen, die innerhalb dieser Mapper-Level-Persistenz-Events nicht unterstützt werden, gehören:
Mapped collection append, add, remove, delete, discard, etc.
Mapped relationship attribute set/del events, d.h.
someobject.related = someotherobject
Der Grund, warum die Connection übergeben wird, ist, dass es ratsam ist, dass einfache SQL-Operationen hier stattfinden, direkt auf der Connection, wie z. B. das Inkrementieren von Zählern oder das Einfügen zusätzlicher Zeilen in Log-Tabellen.
Es gibt auch viele pro-Objekt-Operationen, die gar nicht erst in einem Flush-Event behandelt werden müssen. Die häufigste Alternative ist es, einfach zusätzliche Zustände zusammen mit einem Objekt in seiner __init__()-Methode einzurichten, z. B. das Erstellen zusätzlicher Objekte, die mit dem neuen Objekt assoziiert werden sollen. Die Verwendung von Validatoren, wie in Simple Validators beschrieben, ist ein weiterer Ansatz; diese Funktionen können Änderungen an Attributen abfangen und als Reaktion auf die Attributänderung zusätzliche Zustandsänderungen am Zielobjekt vornehmen. Mit beiden Ansätzen befindet sich das Objekt im richtigen Zustand, noch bevor es den Flush-Schritt erreicht.
Object Lifecycle Events¶
Ein weiterer Anwendungsfall für Events ist die Verfolgung des Lebenszyklus von Objekten. Dies bezieht sich auf die Zustände, die zuerst unter Quickie Intro to Object States eingeführt wurden.
Alle oben genannten Zustände können vollständig mit Events nachverfolgt werden. Jedes Event repräsentiert einen eindeutigen Zustandsübergang, das bedeutet, sowohl der Startzustand als auch der Zielzustand sind Teil dessen, was verfolgt wird. Mit Ausnahme des anfänglichen transienten Events beziehen sich alle Events auf das Objekt oder die Klasse Session, was bedeutet, dass sie entweder mit einem bestimmten Session-Objekt assoziiert werden können.
from sqlalchemy import event
from sqlalchemy.orm import Session
session = Session()
@event.listens_for(session, "transient_to_pending")
def object_is_pending(session, obj):
print("new pending: %s" % obj)Oder mit der Session-Klasse selbst, sowie mit einem bestimmten sessionmaker, was wahrscheinlich die nützlichste Form ist.
from sqlalchemy import event
from sqlalchemy.orm import sessionmaker
maker = sessionmaker()
@event.listens_for(maker, "transient_to_pending")
def object_is_pending(session, obj):
print("new pending: %s" % obj)Die Listener können natürlich auf eine Funktion gestapelt werden, was wahrscheinlich üblich ist. Um beispielsweise alle Objekte zu verfolgen, die in den persistenten Zustand übergehen.
@event.listens_for(maker, "pending_to_persistent")
@event.listens_for(maker, "deleted_to_persistent")
@event.listens_for(maker, "detached_to_persistent")
@event.listens_for(maker, "loaded_as_persistent")
def detect_all_persistent(session, instance):
print("object is now persistent: %s" % instance)Transient¶
Alle gemappten Objekte starten beim ersten Erstellen als transient. In diesem Zustand existiert das Objekt isoliert und hat keine Verbindung zu einer Session. Für diesen anfänglichen Zustand gibt es kein spezifisches "Übergangs"-Event, da keine Session vorhanden ist. Wenn man jedoch abfangen möchte, wann ein transients Objekt erstellt wird, ist die Methode InstanceEvents.init() wahrscheinlich das beste Event. Dieses Event wird auf eine bestimmte Klasse oder Superklasse angewendet. Zum Beispiel, um alle neuen Objekte für eine bestimmte deklarative Basis abzufangen.
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy import event
class Base(DeclarativeBase):
pass
@event.listens_for(Base, "init", propagate=True)
def intercept_init(instance, args, kwargs):
print("new transient: %s" % instance)Transient to Pending¶
Das transiente Objekt wird zum pending, wenn es zum ersten Mal über die Methode Session.add() oder Session.add_all() mit einer Session assoziiert wird. Ein Objekt kann auch als Teil einer Session werden, als Ergebnis eines "Cascade" von einem referenzierenden Objekt, das explizit hinzugefügt wurde. Der Übergang von transient zu pending ist mit dem Event SessionEvents.transient_to_pending() erkennbar.
@event.listens_for(sessionmaker, "transient_to_pending")
def intercept_transient_to_pending(session, object_):
print("transient to pending: %s" % object_)Pending to Persistent¶
Das pending-Objekt wird persistent, wenn ein Flush durchgeführt wird und eine INSERT-Anweisung für die Instanz erfolgt. Das Objekt hat nun einen Identitätsschlüssel. Verfolgen Sie pending zu persistent mit dem Event SessionEvents.pending_to_persistent().
@event.listens_for(sessionmaker, "pending_to_persistent")
def intercept_pending_to_persistent(session, object_):
print("pending to persistent: %s" % object_)Pending zu Transient¶
Das pending Objekt kann zu transient zurückkehren, wenn die Methode Session.rollback() aufgerufen wird, bevor das pending Objekt geflusht wurde, oder wenn die Methode Session.expunge() für das Objekt aufgerufen wird, bevor es geflusht wurde. Verfolgen Sie pending zu transient mit dem Ereignis SessionEvents.pending_to_transient()
@event.listens_for(sessionmaker, "pending_to_transient")
def intercept_pending_to_transient(session, object_):
print("transient to pending: %s" % object_)Als Persistent geladen¶
Objekte können direkt im persistenten Zustand in die Session geladen werden, wenn sie aus der Datenbank geladen werden. Das Verfolgen dieses Zustandsübergangs ist gleichbedeutend mit dem Verfolgen von Objekten, während sie geladen werden, und ist gleichbedeutend mit der Verwendung des Instanz-Ereignisses InstanceEvents.load(). Das Ereignis SessionEvents.loaded_as_persistent() wird jedoch als sessionszentrierter Hook bereitgestellt, um Objekte abzufangen, wenn sie auf diesem speziellen Weg in den persistenten Zustand eintreten.
@event.listens_for(sessionmaker, "loaded_as_persistent")
def intercept_loaded_as_persistent(session, object_):
print("object loaded into persistent state: %s" % object_)Persistent zu Transient¶
Das persistente Objekt kann in den transienten Zustand zurückkehren, wenn die Methode Session.rollback() für eine Transaktion aufgerufen wird, in der das Objekt ursprünglich als pending hinzugefügt wurde. Im Falle von ROLLBACK wird die INSERT-Anweisung, die dieses Objekt persistent gemacht hat, zurückgerollt, und das Objekt wird aus der Session entfernt, um wieder transient zu werden. Verfolgen Sie Objekte, die von persistent zu transient zurückgerollt wurden, mit dem Ereignis-Hook SessionEvents.persistent_to_transient()
@event.listens_for(sessionmaker, "persistent_to_transient")
def intercept_persistent_to_transient(session, object_):
print("persistent to transient: %s" % object_)Persistent zu Gelöscht¶
Das persistente Objekt gelangt in den gelöschten Zustand, wenn ein zum Löschen markiertes Objekt während des Flush-Vorgangs aus der Datenbank gelöscht wird. Beachten Sie, dass dies **nicht dasselbe** ist wie das Aufrufen der Methode Session.delete() für ein Zielobjekt. Die Methode Session.delete() **markiert** das Objekt nur zum Löschen; die eigentliche DELETE-Anweisung wird erst ausgegeben, wenn der Flush fortgesetzt wird. Erst nach dem Flush ist der "gelöschte" Zustand für das Zielobjekt vorhanden.
Im "gelöschten" Zustand ist das Objekt nur marginal mit der Session verbunden. Es ist weder in der Identitätsabbildung noch in der Sammlung Session.deleted, die sich auf den Zeitpunkt bezieht, zu dem es zum Löschen pending war, enthalten.
Aus dem "gelöschten" Zustand kann das Objekt entweder in den getrennten Zustand übergehen, wenn die Transaktion committed wird, oder zurück in den persistenten Zustand, wenn die Transaktion stattdessen zurückgerollt wird.
Verfolgen Sie den Übergang von persistent zu gelöscht mit SessionEvents.persistent_to_deleted()
@event.listens_for(sessionmaker, "persistent_to_deleted")
def intercept_persistent_to_deleted(session, object_):
print("object was DELETEd, is now in deleted state: %s" % object_)Gelöscht zu Detached¶
Das gelöschte Objekt wird detached, wenn die Transaktion der Session committed wird. Nach dem Aufruf der Methode Session.commit() ist die Datenbanktransaktion endgültig, und die Session verwirft das gelöschte Objekt vollständig und entfernt alle Verknüpfungen dazu. Verfolgen Sie den Übergang von gelöscht zu detached mit SessionEvents.deleted_to_detached()
@event.listens_for(sessionmaker, "deleted_to_detached")
def intercept_deleted_to_detached(session, object_):
print("deleted to detached: %s" % object_)Hinweis
Während sich das Objekt im gelöschten Zustand befindet, gibt das Attribut InstanceState.deleted, zugänglich über inspect(object).deleted, True zurück. Wenn das Objekt jedoch detached ist, gibt InstanceState.deleted wieder False zurück. Um zu erkennen, dass ein Objekt gelöscht wurde, unabhängig davon, ob es detached ist oder nicht, verwenden Sie den Zugriff auf InstanceState.was_deleted.
Persistent zu Detached¶
Das persistente Objekt wird detached, wenn das Objekt von der Session getrennt wird, über die Methoden Session.expunge(), Session.expunge_all() oder Session.close().
Hinweis
Ein Objekt kann auch **implizit detached** werden, wenn seine besitzende Session von der Anwendung dereferenziert und aufgrund der Garbage Collection verworfen wird. In diesem Fall **wird kein Ereignis ausgelöst**.
Verfolgen Sie Objekte, während sie von persistent zu detached wechseln, mit dem Ereignis SessionEvents.persistent_to_detached()
@event.listens_for(sessionmaker, "persistent_to_detached")
def intercept_persistent_to_detached(session, object_):
print("object became detached: %s" % object_)Detached zu Persistent¶
Das detached Objekt wird persistent, wenn es mit einer Session mithilfe der Methode Session.add() oder einer äquivalenten Methode wieder mit einer Session verknüpft wird. Verfolgen Sie Objekte, die von detached zurück zu persistent wechseln, mit dem Ereignis SessionEvents.detached_to_persistent()
@event.listens_for(sessionmaker, "detached_to_persistent")
def intercept_detached_to_persistent(session, object_):
print("object became persistent again: %s" % object_)Gelöscht zu Persistent¶
Das gelöschte Objekt kann in den persistenten Zustand zurückversetzt werden, wenn die Transaktion, in der es GELÖSCHT wurde, mit der Methode Session.rollback() zurückgerollt wurde. Verfolgen Sie gelöschte Objekte, die in den persistenten Zustand zurückkehren, mit dem Ereignis SessionEvents.deleted_to_persistent()
@event.listens_for(sessionmaker, "deleted_to_persistent")
def intercept_deleted_to_persistent(session, object_):
print("deleted to persistent: %s" % object_)Transaktionsereignisse¶
Transaktionsereignisse ermöglichen es einer Anwendung, benachrichtigt zu werden, wenn Transaktionsgrenzen auf der Ebene der Session auftreten, sowie wenn die Session den transaktionalen Zustand auf Connection-Objekten ändert.
SessionEvents.after_transaction_create(),SessionEvents.after_transaction_end()- Diese Ereignisse verfolgen die logischen Transaktionsbereiche derSessionauf eine Weise, die nicht spezifisch für einzelne Datenbankverbindungen ist. Diese Ereignisse sollen bei der Integration von Transaktionsverfolgungssystemen wiezope.sqlalchemyhelfen. Verwenden Sie diese Ereignisse, wenn die Anwendung einen externen Bereich mit dem transaktionalen Bereich derSessionabgleichen muss. Diese Hooks spiegeln das "verschachtelte" transaktionale Verhalten derSessionwider, indem sie logische "Untertransaktionen" sowie "verschachtelte" (z. B. SAVEPOINT) Transaktionen verfolgen.SessionEvents.before_commit(),SessionEvents.after_commit(),SessionEvents.after_begin(),SessionEvents.after_rollback(),SessionEvents.after_soft_rollback()- Diese Ereignisse ermöglichen die Verfolgung von Transaktionsereignissen aus der Perspektive von Datenbankverbindungen.SessionEvents.after_begin()ist insbesondere ein Ereignis pro Verbindung; eineSession, die mehr als eine Verbindung unterhält, löst dieses Ereignis für jede Verbindung einzeln aus, sobald diese Verbindungen innerhalb der aktuellen Transaktion verwendet werden. Die Rollback- und Commit-Ereignisse beziehen sich dann darauf, wann die DBAPI-Verbindungen selbst direkt Rollback- oder Commit-Anweisungen erhalten haben.
Attributänderungsereignisse¶
Die Attributänderungsereignisse ermöglichen das Abfangen, wann bestimmte Attribute eines Objekts geändert werden. Diese Ereignisse umfassen AttributeEvents.set(), AttributeEvents.append() und AttributeEvents.remove(). Diese Ereignisse sind äußerst nützlich, insbesondere für objektindividuelle Validierungsoperationen; es ist jedoch oft viel bequemer, einen "Validator"-Hook zu verwenden, der diese Hooks im Hintergrund nutzt; siehe Einfache Validatoren für Hintergrundinformationen dazu. Die Attributereignisse sind auch hinter den Mechanismen von Backreferences. Ein Beispiel, das die Verwendung von Attributereignissen veranschaulicht, finden Sie in Attribut-Instrumentierung.
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