Ereignisse

SQLAlchemy enthält eine Ereignis-API, die eine Vielzahl von Hooks in die Interna von SQLAlchemy Core und ORM veröffentlicht.

Ereignisregistrierung

Die Anmeldung zu einem Ereignis erfolgt über einen einzigen API-Punkt, die Funktion listen(), oder alternativ über den Dekorator listens_for(). Diese Funktionen akzeptieren ein Ziel, einen String-Identifikator, der das abzufangende Ereignis identifiziert, und eine vom Benutzer definierte Listener-Funktion. Zusätzliche Positions- und Schlüsselwortargumente für diese beiden Funktionen können von spezifischen Ereignistypen unterstützt werden, die alternative Schnittstellen für die gegebene Ereignisfunktion angeben oder Anweisungen bezüglich sekundärer Ereignisziele basierend auf dem gegebenen Ziel liefern können.

Der Name eines Ereignisses und die Argumentensignatur einer entsprechenden Listener-Funktion werden von einer klassengebundenen Spezifikationsmethode abgeleitet, die an eine Markierungsklasse gebunden ist, die in der Dokumentation beschrieben ist. Zum Beispiel gibt die Dokumentation für PoolEvents.connect() an, dass der Ereignisname "connect" lautet und dass eine vom Benutzer definierte Listener-Funktion zwei Positionsargumente erhalten sollte

from sqlalchemy.event import listen
from sqlalchemy.pool import Pool


def my_on_connect(dbapi_con, connection_record):
    print("New DBAPI connection:", dbapi_con)


listen(Pool, "connect", my_on_connect)

Die Anmeldung mit dem Dekorator listens_for() sieht so aus

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect")
def my_on_connect(dbapi_con, connection_record):
    print("New DBAPI connection:", dbapi_con)

Stil für benannte Argumente

Es gibt verschiedene Arten von Argumentenstilen, die von Listener-Funktionen akzeptiert werden können. Am Beispiel von PoolEvents.connect() empfängt diese Funktion die Argumente dbapi_connection und connection_record. Wir können diese Argumente nach Namen empfangen, indem wir eine Listener-Funktion einrichten, die **keyword Argumente akzeptiert, indem wir named=True entweder an listen() oder listens_for() übergeben

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect", named=True)
def my_on_connect(**kw):
    print("New DBAPI connection:", kw["dbapi_connection"])

Bei der Verwendung von benanntem Argument-Passing werden die in der Funktionsargumentenspezifikation aufgeführten Namen als Schlüssel im Wörterbuch verwendet.

Der benannte Stil übergibt alle Argumente nach Namen, unabhängig von der Funktionssignatur, so dass spezifische Argumente auch in beliebiger Reihenfolge aufgeführt werden können, solange die Namen übereinstimmen

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect", named=True)
def my_on_connect(dbapi_connection, **kw):
    print("New DBAPI connection:", dbapi_connection)
    print("Connection record:", kw["connection_record"])

Oben teilt die Anwesenheit von **kw listens_for() mit, dass Argumente nach Namen und nicht positionsweise an die Funktion übergeben werden sollen.

Ziele

Die Funktion listen() ist in Bezug auf Ziele sehr flexibel. Sie akzeptiert im Allgemeinen Klassen, Instanzen dieser Klassen sowie verwandte Klassen oder Objekte, von denen das entsprechende Ziel abgeleitet werden kann. Zum Beispiel akzeptiert das oben erwähnte "connect"-Ereignis Engine-Klassen und -Objekte sowie Pool-Klassen und -Objekte

from sqlalchemy.event import listen
from sqlalchemy.pool import Pool, QueuePool
from sqlalchemy import create_engine
from sqlalchemy.engine import Engine
import psycopg2


def connect():
    return psycopg2.connect(user="ed", host="127.0.0.1", dbname="test")


my_pool = QueuePool(connect)
my_engine = create_engine("postgresql+psycopg2://ed@localhost/test")

# associate listener with all instances of Pool
listen(Pool, "connect", my_on_connect)

# associate listener with all instances of Pool
# via the Engine class
listen(Engine, "connect", my_on_connect)

# associate listener with my_pool
listen(my_pool, "connect", my_on_connect)

# associate listener with my_engine.pool
listen(my_engine, "connect", my_on_connect)

Modifikatoren

Einige Listener erlauben die Übergabe von Modifikatoren an listen(). Diese Modifikatoren bieten manchmal alternative Aufrufsignaturen für Listener. Wie bei ORM-Ereignissen können einige Ereignis-Listener einen Rückgabewert haben, der die nachfolgende Verarbeitung modifiziert. Standardmäßig erfordert kein Listener einen Rückgabewert, aber durch die Übergabe von retval=True kann dieser Wert unterstützt werden

def validate_phone(target, value, oldvalue, initiator):
    """Strip non-numeric characters from a phone number"""

    return re.sub(r"\D", "", value)


# setup listener on UserContact.phone attribute, instructing
# it to use the return value
listen(UserContact.phone, "set", validate_phone, retval=True)

Ereignisse und Multiprocessing

Die Ereignis-Hooks von SQLAlchemy werden mit Python-Funktionen und -Objekten implementiert, sodass sich Ereignisse über Python-Funktionsaufrufe verbreiten. Python-Multiprocessing folgt der gleichen Denkweise wie OS-Multiprocessing, z. B. einem Elternprozess, der einen Kindprozess forkt. Daher können wir das Verhalten des SQLAlchemy-Ereignissystems anhand desselben Modells beschreiben.

In einem Elternprozess registrierte Ereignis-Hooks sind in neuen Kindprozessen vorhanden, die nach der Registrierung der Hooks von diesem Elternteil ausgehen, da der Kindprozess beim Start eine Kopie aller vorhandenen Python-Strukturen vom Elternteil erhält. Kindprozesse, die bereits vor der Registrierung der Hooks existieren, erhalten diese neuen Ereignis-Hooks nicht, da Änderungen an Python-Strukturen in einem Elternprozess nicht an Kindprozesse weitergegeben werden.

Die Ereignisse selbst sind Python-Funktionsaufrufe, die keine Möglichkeit haben, sich zwischen Prozessen zu verbreiten. Das Ereignissystem von SQLAlchemy implementiert keine Interprozesskommunikation. Es ist möglich, Ereignis-Hooks zu implementieren, die Python-Interprozessnachrichten innerhalb von ihnen verwenden, dies müsste jedoch vom Benutzer implementiert werden.

Ereignisreferenz

Sowohl SQLAlchemy Core als auch SQLAlchemy ORM bieten eine breite Palette von Ereignis-Hooks

  • Core-Ereignisse – diese werden in Core Events beschrieben und umfassen Ereignis-Hooks, die spezifisch für den Lebenszyklus des Verbindungspools, die Ausführung von SQL-Anweisungen, den Transaktionslebenszyklus sowie die Erstellung und das Abbauen von Schemata sind.

  • ORM-Ereignisse – diese werden in ORM Events beschrieben und umfassen Ereignis-Hooks, die spezifisch für die Instrumentierung von Klassen und Attributen, Hooks zur Objektinitialisierung, Hooks zum Ändern von Attributen, Sitzungsstatus, Flush- und Commit-Hooks, Mapper-Initialisierung, Objekt-/Ergebnispopulation und Per-Instanz-Persistenz-Hooks sind.

API-Referenz

Objektname Beschreibung

contains(target, identifier, fn)

Gibt True zurück, wenn das angegebene Ziel/Identifikator/fn zum Zuhören eingerichtet ist.

listen(target, identifier, fn, *args, **kw)

Registriert eine Listener-Funktion für das angegebene Ziel.

listens_for(target, identifier, *args, **kw)

Dekoriert eine Funktion als Listener für das angegebene Ziel + Identifikator.

remove(target, identifier, fn)

Entfernt einen Ereignis-Listener.

function sqlalchemy.event.listen(target: Any, identifier: str, fn: Callable[[...], Any], *args: Any, **kw: Any) None

Registriert eine Listener-Funktion für das angegebene Ziel.

Die Funktion listen() ist Teil der primären Schnittstelle für das Ereignissystem von SQLAlchemy, dokumentiert unter Events.

z. B.

from sqlalchemy import event
from sqlalchemy.schema import UniqueConstraint


def unique_constraint_name(const, table):
    const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)


event.listen(
    UniqueConstraint, "after_parent_attach", unique_constraint_name
)
Parameter:
  • insert (bool) – Das Standardverhalten für Ereignishandler besteht darin, die dekorierte, benutzerdefinierte Funktion beim Entdecken an eine interne Liste registrierter Ereignis-Listener anzuhängen. Wenn ein Benutzer eine Funktion mit insert=True registriert, fügt SQLAlchemy (stellt voran) die Funktion beim Entdecken in die interne Liste ein. Dieses Feature wird von den SQLAlchemy-Maintainern normalerweise nicht verwendet oder empfohlen, wird aber bereitgestellt, um sicherzustellen, dass bestimmte benutzerdefinierte Funktionen vor anderen ausgeführt werden können, z. B. beim Ändern des sql_mode in MySQL.

  • named (bool) – Bei Verwendung von benanntem Argument-Passing werden die in der Funktionsargumentenspezifikation aufgeführten Namen als Schlüssel im Wörterbuch verwendet. Siehe Named Argument Styles.

  • once (bool) – Private/interne API-Nutzung. Veraltet. Dieser Parameter würde gewährleisten, dass eine Ereignisfunktion nur einmal pro angegebenem Ziel ausgeführt wird. Er impliziert jedoch keine automatische De-Registrierung der Listener-Funktion; die Zuordnung einer beliebig hohen Anzahl von Listenern ohne explizites Entfernen führt zu unbegrenztem Speicherwachstum, auch wenn once=True angegeben ist.

  • propagate (bool) – Das Schlüsselwortargument propagate ist bei der Arbeit mit ORM-Instrumentierung und Mapping-Ereignissen verfügbar. Siehe MapperEvents und MapperEvents.before_mapper_configured() für Beispiele.

  • retval (bool) –

    Dieses Flag gilt nur für bestimmte Ereignis-Listener, von denen jeder eine Dokumentation enthält, die erklärt, wann er verwendet werden sollte. Standardmäßig erfordert kein Listener einen Rückgabewert. Einige Listener unterstützen jedoch spezielle Verhaltensweisen für Rückgabewerte und geben in ihrer Dokumentation an, dass das Flag retval=True erforderlich ist, damit ein Rückgabewert verarbeitet werden kann.

    Ereignis-Listener-Suiten, die listen.retval verwenden, umfassen ConnectionEvents und AttributeEvents.

Hinweis

Die Funktion listen() kann nicht gleichzeitig aufgerufen werden, während das Zielereignis ausgeführt wird. Dies hat Auswirkungen auf die Thread-Sicherheit und bedeutet auch, dass ein Ereignis nicht innerhalb der Listener-Funktion für sich selbst hinzugefügt werden kann. Die Liste der auszuführenden Ereignisse befindet sich in einer veränderlichen Sammlung, die während der Iteration nicht geändert werden kann.

Ereignisregistrierung und -entfernung sind nicht als "Hochgeschwindigkeitsoperationen" gedacht; sie sind Konfigurationsoperationen. Für Systeme, die sich schnell und in großem Umfang mit Ereignissen verbinden und trennen müssen, verwenden Sie eine veränderliche Struktur, die von einer einzelnen Listener-Funktion aus verwaltet wird.

Siehe auch

listens_for()

remove()

function sqlalchemy.event.listens_for(target: Any, identifier: str, *args: Any, **kw: Any) Callable[[Callable[[...], Any]], Callable[[...], Any]]

Dekoriert eine Funktion als Listener für das angegebene Ziel + Identifikator.

Der Dekorator listens_for() ist Teil der primären Schnittstelle für das Ereignissystem von SQLAlchemy, dokumentiert unter Events.

Diese Funktion teilt im Allgemeinen die gleichen Schlüsselwortargumente wie listen().

z. B.

from sqlalchemy import event
from sqlalchemy.schema import UniqueConstraint


@event.listens_for(UniqueConstraint, "after_parent_attach")
def unique_constraint_name(const, table):
    const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)

Eine gegebene Funktion kann auch nur für die erste Ausführung des Ereignisses mit dem Argument once aufgerufen werden

@event.listens_for(Mapper, "before_configure", once=True)
def on_config():
    do_config()

Warnung

Das Argument once impliziert keine automatische De-Registrierung der Listener-Funktion, nachdem sie zum ersten Mal aufgerufen wurde; ein Listener-Eintrag bleibt mit dem Zielobjekt verbunden. Die Zuordnung einer beliebig hohen Anzahl von Listenern ohne explizites Entfernen führt zu unbegrenztem Speicherwachstum, auch wenn once=True angegeben ist.

Siehe auch

listen() – allgemeine Beschreibung des Ereignis-Listenings

function sqlalchemy.event.remove(target: Any, identifier: str, fn: Callable[[...], Any]) None

Entfernt einen Ereignis-Listener.

Die Argumente hier sollten exakt denen entsprechen, die an listen() übergeben wurden; alle Registrierungen von Ereignissen, die als Ergebnis dieses Aufrufs erfolgten, werden durch den Aufruf von remove() mit denselben Argumenten rückgängig gemacht.

z. B.

# if a function was registered like this...
@event.listens_for(SomeMappedClass, "before_insert", propagate=True)
def my_listener_function(*arg):
    pass


# ... it's removed like this
event.remove(SomeMappedClass, "before_insert", my_listener_function)

Oben wurde die Listener-Funktion, die mit SomeMappedClass verbunden war, auch an Unterklassen von SomeMappedClass weitergegeben; die Funktion remove() wird all diese Operationen rückgängig machen.

Hinweis

Die Funktion remove() kann nicht gleichzeitig aufgerufen werden, während das Zielereignis ausgeführt wird. Dies hat Auswirkungen auf die Thread-Sicherheit und bedeutet auch, dass ein Ereignis nicht innerhalb der Listener-Funktion für sich selbst entfernt werden kann. Die Liste der auszuführenden Ereignisse befindet sich in einer veränderlichen Sammlung, die während der Iteration nicht geändert werden kann.

Ereignisregistrierung und -entfernung sind nicht als "Hochgeschwindigkeitsoperationen" gedacht; sie sind Konfigurationsoperationen. Für Systeme, die sich schnell und in großem Umfang mit Ereignissen verbinden und trennen müssen, verwenden Sie eine veränderliche Struktur, die von einer einzelnen Listener-Funktion aus verwaltet wird.

Siehe auch

listen()

function sqlalchemy.event.contains(target: Any, identifier: str, fn: Callable[[...], Any]) bool

Gibt True zurück, wenn das angegebene Ziel/Identifikator/fn zum Zuhören eingerichtet ist.