ORM Mapped Class Übersicht

Übersicht über die ORM-Klassenmapping-Konfiguration.

Für Leser, die neu im SQLAlchemy ORM sind und/oder neu in Python im Allgemeinen, wird empfohlen, die ORM Schnellstartanleitung durchzulesen und vorzugsweise das SQLAlchemy Unified Tutorial zu bearbeiten, wo die ORM-Konfiguration zuerst unter Verwendung von ORM Deklarativen Formularen zur Definition von Tabellenmetadaten eingeführt wird.

ORM Mapping-Stile

SQLAlchemy bietet zwei verschiedene Stile der Mapper-Konfiguration, die dann weitere Unteroptionen für ihre Einrichtung bieten. Die Variabilität der Mapper-Stile ist vorhanden, um einer vielfältigen Liste von Entwicklerpräferenzen gerecht zu werden, einschließlich des Abstraktionsgrades einer benutzerdefinierten Klasse von der Art und Weise, wie sie auf relationale Schematabellen und -spalten abgebildet werden soll, welche Arten von Klassenhierarchien verwendet werden, einschließlich der Frage, ob benutzerdefinierte Metaklassen-Schemata vorhanden sind oder nicht, und schließlich, ob andere Klasseninstrumentierungsansätze vorhanden sind, wie z. B. die gleichzeitige Verwendung von Python Datenklassen.

In modernem SQLAlchemy ist der Unterschied zwischen diesen Stilen größtenteils oberflächlich; wenn ein bestimmter SQLAlchemy-Konfigurationsstil verwendet wird, um die Absicht, eine Klasse abzubilden, auszudrücken, läuft der interne Prozess der Abbildung der Klasse für jeden Stil größtenteils gleich ab, wobei das Endergebnis immer eine benutzerdefinierte Klasse ist, gegen die ein Mapper konfiguriert ist, mit einer wählbaren Einheit, die typischerweise durch ein Table-Objekt dargestellt wird, und die Klasse selbst wurde instrumentiert, um Verhaltensweisen zu integrieren, die mit relationalen Operationen sowohl auf der Ebene der Klasse als auch auf Instanzen dieser Klasse verbunden sind. Da der Prozess in allen Fällen im Grunde gleich ist, sind Klassen, die aus verschiedenen Stilen abgebildet werden, immer vollständig interoperabel. Das Protokoll MappedClassProtocol kann verwendet werden, um eine gemappte Klasse bei der Verwendung von Typ-Checkern wie mypy anzuzeigen.

Die ursprüngliche Mapping-API wird allgemein als „klassischer“ Stil bezeichnet, während der stärker automatisierte Mapping-Stil als „deklarativer“ Stil bekannt ist. SQLAlchemy bezeichnet diese beiden Mapping-Stile nun als imperatives Mapping und deklaratives Mapping.

Unabhängig vom verwendeten Mapping-Stil stammen alle ORM-Mappings ab SQLAlchemy 1.4 von einem einzigen Objekt namens registry, einer Registrierung von gemappten Klassen. Mithilfe dieser Registrierung kann eine Reihe von Mapper-Konfigurationen als Gruppe finalisiert werden, und Klassen innerhalb einer bestimmten Registrierung können sich während des Konfigurationsprozesses gegenseitig namentlich beziehen.

Geändert in Version 1.4: Deklaratives und klassisches Mapping werden nun als „deklaratives“ und „imperatives“ Mapping bezeichnet und sind intern vereinheitlicht, wobei alle von dem registry-Konstrukt stammen, das eine Sammlung von zusammenhängenden Mappings darstellt.

Deklaratives Mapping

Das Deklarative Mapping ist die typische Art und Weise, wie Mappings in modernem SQLAlchemy erstellt werden. Das gängigste Muster ist, zuerst eine Basisklasse mithilfe der DeclarativeBase-Oberklasse zu erstellen. Die resultierende Basisklasse wendet beim Unterklassifizieren den deklarativen Mapping-Prozess auf alle von ihr abgeleiteten Unterklassen an, relativ zu einer bestimmten registry, die standardmäßig lokal zur neuen Basis ist. Das folgende Beispiel veranschaulicht die Verwendung einer deklarativen Basis, die dann in einem deklarativen Tabellenmapping verwendet wird.

from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


# declarative base class
class Base(DeclarativeBase):
    pass


# an example mapping using the base
class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    fullname: Mapped[str] = mapped_column(String(30))
    nickname: Mapped[Optional[str]]

Oben wird die Klasse DeclarativeBase verwendet, um eine neue Basisklasse zu generieren (in der SQLAlchemy-Dokumentation wird sie typischerweise als Base bezeichnet, kann aber jeden gewünschten Namen haben), von der neue abzubildende Klassen erben können, wie oben eine neue gemappte Klasse User konstruiert wird.

Geändert in Version 2.0: Die DeclarativeBase-Oberklasse ersetzt die Verwendung der Funktion declarative_base() und der Methoden registry.generate_base(); der Oberklassen-Ansatz integriert sich mit PEP 484-Tools ohne die Verwendung von Plugins. Siehe ORM Deklarative Modelle für Migrationshinweise.

Die Basisklasse bezieht sich auf ein registry-Objekt, das eine Sammlung von zusammenhängenden gemappten Klassen verwaltet, sowie auf ein MetaData-Objekt, das eine Sammlung von Table-Objekten enthält, auf die die Klassen abgebildet werden.

Die wichtigsten deklarativen Mapping-Stile werden in den folgenden Abschnitten näher erläutert.

Innerhalb des Geltungsbereichs einer deklarativ gemappten Klasse gibt es auch zwei Varianten, wie die Table-Metadaten deklariert werden können. Diese umfassen:

  • Deklarative Tabelle mit mapped_column() – Tabellenspalten werden inline innerhalb der gemappten Klasse mit der mapped_column()-Direktive (oder in älterer Form mit dem Column-Objekt direkt) deklariert. Die mapped_column()-Direktive kann auch optional mit Typ-Annotationen unter Verwendung der Klasse Mapped kombiniert werden, die einige Details über die gemappten Spalten direkt liefern kann. Die Spaltendirektiven in Kombination mit den Klassenattributen __tablename__ und optional __table_args__ ermöglichen dem deklarativen Mapping-Prozess die Erstellung eines Table-Objekts, das abgebildet werden soll.

  • Deklarativ mit imperativer Tabelle (auch bekannt als Hybrid-Deklarativ) – Anstatt Tabellenname und Attribute separat anzugeben, wird ein explizit erstelltes Table-Objekt mit einer Klasse verknüpft, die ansonsten deklarativ abgebildet wird. Dieser Mapping-Stil ist eine Hybridform aus „deklarativem“ und „imperativem“ Mapping und gilt für Techniken wie das Abbilden von Klassen auf reflektierte Table-Objekte, sowie das Abbilden von Klassen auf bestehende Core-Konstrukte wie Joins und Subqueries.

Die Dokumentation für deklaratives Mapping wird unter Klassen mit Deklarativem Mappen fortgesetzt.

Imperatives Mapping

Ein imperatives oder klassisches Mapping bezieht sich auf die Konfiguration einer gemappten Klasse mithilfe der Methode registry.map_imperatively(), bei der die Zielklasse keine deklarativen Klassenattribute enthält.

Tipp

Die imperative Mapping-Form ist eine seltenere Mapping-Form, die aus den allerersten SQLAlchemy-Versionen im Jahr 2006 stammt. Sie ist im Wesentlichen ein Mittel, um das deklarative System zu umgehen, um ein „rudimentäreres“ Mapping-System bereitzustellen, und bietet keine modernen Funktionen wie PEP 484-Unterstützung. Daher verwenden die meisten Dokumentationsbeispiele deklarative Formen, und es wird empfohlen, dass neue Benutzer mit der Deklarativen Tabellenkonfiguration beginnen.

Geändert in Version 2.0: Die Methode registry.map_imperatively() wird nun verwendet, um klassische Mappings zu erstellen. Die eigenständige Funktion sqlalchemy.orm.mapper() ist effektiv entfernt.

In „klassischer“ Form werden die Tabellenmetadaten separat mit dem Table-Konstrukt erstellt und dann über die Methode registry.map_imperatively() mit der User-Klasse verknüpft, nachdem eine registry-Instanz eingerichtet wurde. Normalerweise wird eine einzelne Instanz von registry für alle gemappten Klassen gemeinsam genutzt, die miteinander zusammenhängen.

from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import registry

mapper_registry = registry()

user_table = Table(
    "user",
    mapper_registry.metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
    Column("fullname", String(50)),
    Column("nickname", String(12)),
)


class User:
    pass


mapper_registry.map_imperatively(User, user_table)

Informationen über gemappte Attribute, wie z. B. Beziehungen zu anderen Klassen, werden über das properties-Dictionary bereitgestellt. Das folgende Beispiel veranschaulicht ein zweites Table-Objekt, das einer Klasse namens Address zugeordnet ist, die dann über relationship() mit User verknüpft wird.

address = Table(
    "address",
    metadata_obj,
    Column("id", Integer, primary_key=True),
    Column("user_id", Integer, ForeignKey("user.id")),
    Column("email_address", String(50)),
)

mapper_registry.map_imperatively(
    User,
    user,
    properties={
        "addresses": relationship(Address, backref="user", order_by=address.c.id)
    },
)

mapper_registry.map_imperatively(Address, address)

Beachten Sie, dass Klassen, die mit dem imperativen Ansatz gemappt werden, vollständig austauschbar mit denen sind, die mit dem deklarativen Ansatz gemappt werden. Beide Systeme erstellen letztendlich dieselbe Konfiguration, bestehend aus einer Table, einer benutzerdefinierten Klasse, die mit einem Mapper-Objekt verknüpft ist. Wenn wir über „das Verhalten von Mapper“ sprechen, schließt dies auch die Verwendung des deklarativen Systems ein – es wird immer noch verwendet, nur im Hintergrund.

Wesentliche Komponenten einer gemappten Klasse

Bei allen Mapping-Formen kann das Mapping der Klasse auf viele Arten konfiguriert werden, indem Konstruktionsargumente übergeben werden, die letztendlich Teil des Mapper-Objekts über dessen Konstruktor werden. Die an Mapper gelieferten Parameter stammen aus der gegebenen Mapping-Form, einschließlich der an registry.map_imperatively() übergebenen Parameter für ein imperatives Mapping oder, bei Verwendung des deklarativen Systems, aus einer Kombination der Tabellenspalten, SQL-Ausdrücke und Beziehungen, die zusammen mit Attributen wie __mapper_args__ abgebildet werden.

Es gibt vier allgemeine Klassen von Konfigurationsinformationen, nach denen die Klasse Mapper sucht:

Die zu mappende Klasse

Dies ist eine Klasse, die wir in unserer Anwendung erstellen. An die Struktur dieser Klasse gibt es im Allgemeinen keine Einschränkungen. [1] Wenn eine Python-Klasse abgebildet wird, kann es nur **einen** Mapper-Objekt für die Klasse geben. [2]

Beim Mapping mit dem deklarativen Mapping-Stil ist die abzubildende Klasse entweder eine Unterklasse der deklarativen Basisklasse oder wird durch einen Dekorator oder eine Funktion wie registry.mapped() behandelt.

Beim Mapping mit dem imperativen Stil wird die Klasse direkt als Argument map_imperatively.class_ übergeben.

Die Tabelle oder ein anderes FROM-Klausel-Objekt

In den allermeisten gängigen Fällen ist dies eine Instanz von Table. Für fortgeschrittenere Anwendungsfälle kann es auch auf jede Art von FromClause-Objekt verweisen, wobei die gängigsten alternativen Objekte das Subquery- und das Join-Objekt sind.

Beim Mapping mit dem deklarativen Mapping-Stil wird die Zielabelle entweder vom deklarativen System basierend auf dem Attribut __tablename__ und den angebotenen Column-Objekten generiert, oder sie wird über das Attribut __table__ festgelegt. Diese beiden Konfigurationsstile werden unter Deklarative Tabelle mit mapped_column() und Deklarativ mit imperativer Tabelle (auch bekannt als Hybrid-Deklarativ) vorgestellt.

Beim Mapping mit dem imperativen Stil wird die Zielabelle positionsabhängig als Argument map_imperatively.local_table übergeben.

Im Gegensatz zur Anforderung „ein Mapper pro Klasse“ für eine gemappte Klasse kann das Table- oder ein anderes FromClause-Objekt, das Gegenstand des Mappings ist, mit beliebiger Anzahl von Mappings assoziiert werden. Der Mapper wendet Modifikationen direkt auf die benutzerdefinierte Klasse an, modifiziert aber das gegebene Table- oder andere FromClause-Objekte in keiner Weise.

Das Eigenschaften-Dictionary

Dies ist ein Dictionary aller Attribute, die mit der gemappten Klasse verknüpft werden. Standardmäßig generiert der Mapper Einträge für dieses Dictionary, abgeleitet von der gegebenen Table, in Form von ColumnProperty-Objekten, die jeweils auf eine einzelne Column der gemappten Tabelle verweisen. Das Eigenschaften-Dictionary enthält auch alle anderen Arten von MapperProperty-Objekten, die konfiguriert werden sollen, am häufigsten Instanzen, die vom relationship()-Konstrukt generiert werden.

Beim Mapping mit dem deklarativen Mapping-Stil wird das Eigenschaften-Dictionary vom deklarativen System durch Scannen der abzubildenden Klasse nach geeigneten Attributen generiert. Informationen zu diesem Prozess finden Sie im Abschnitt Definieren von gemappten Eigenschaften mit Deklarativem.

Beim Mapping mit dem imperativen Stil wird das Eigenschaften-Dictionary direkt als Parameter properties an registry.map_imperatively() übergeben, welches es an den Parameter Mapper.properties weiterleitet.

Andere Mapper-Konfigurationsparameter

Beim Mapping mit dem deklarativen Mapping-Stil werden zusätzliche Mapper-Konfigurationsargumente über das Klassenattribut __mapper_args__ konfiguriert. Anwendungsbeispiele finden Sie unter Mapper-Konfigurationsoptionen mit Deklarativem.

Beim Mapping mit dem imperativen Stil werden Schlüsselwortargumente an die Methode registry.map_imperatively() übergeben, die sie an die Klasse Mapper weiterleitet.

Die vollständige Bandbreite der akzeptierten Parameter ist unter Mapper dokumentiert.

Verhalten einer gemappten Klasse

Über alle Mapping-Stile hinweg, die das registry-Objekt verwenden, sind die folgenden Verhaltensweisen üblich:

Standardkonstruktor

Die registry wendet einen Standardkonstruktor, d. h. eine __init__-Methode, auf alle gemappten Klassen an, die nicht explizit ihre eigene __init__-Methode haben. Das Verhalten dieser Methode besteht darin, einen bequemen Schlüsselwortkonstruktor bereitzustellen, der als optionale Schlüsselwortargumente alle benannten Attribute akzeptiert. Z. B.

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    fullname: Mapped[str]

Ein Objekt vom Typ User oben hat einen Konstruktor, der die Erstellung von User-Objekten wie folgt ermöglicht:

u1 = User(name="some name", fullname="some fullname")

Tipp

Die Funktion Deklaratives Datenklassen-Mapping bietet eine alternative Möglichkeit zur Generierung einer Standard-__init__()-Methode durch die Verwendung von Python-Datenklassen und ermöglicht eine hochgradig konfigurierbare Konstruktorform.

Warnung

Die __init__()-Methode der Klasse wird nur aufgerufen, wenn das Objekt im Python-Code konstruiert wird, und **nicht, wenn ein Objekt aus der Datenbank geladen oder aktualisiert wird**. Siehe den nächsten Abschnitt Aufrechterhaltung von nicht gemapptem Zustand über Ladevorgänge hinweg für eine Einführung, wie spezielle Logik aufgerufen werden kann, wenn Objekte geladen werden.

Eine Klasse, die eine explizite __init__()-Methode enthält, behält diese Methode bei, und kein Standardkonstruktor wird angewendet.

Um den verwendeten Standardkonstruktor zu ändern, kann ein benutzerdefinierter Python-Callable an den Parameter registry.constructor übergeben werden, der als Standardkonstruktor verwendet wird.

Der Konstruktor gilt auch für imperative Mappings.

from sqlalchemy.orm import registry

mapper_registry = registry()

user_table = Table(
    "user",
    mapper_registry.metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(50)),
)


class User:
    pass


mapper_registry.map_imperatively(User, user_table)

Die obige Klasse, die wie unter Imperatives Mapping beschrieben imperativ gemappt wurde, wird auch den Standardkonstruktor aufweisen, der mit der registry verknüpft ist.

Neu in Version 1.4: Klassische Mappings unterstützen nun einen standardmäßigen Konfigurations-Konstruktor, wenn sie über die Methode registry.map_imperatively() gemappt werden.

Aufrechterhaltung von nicht gemapptem Zustand über Ladevorgänge hinweg

Die __init__()-Methode der gemappten Klasse wird aufgerufen, wenn das Objekt direkt im Python-Code konstruiert wird.

u1 = User(name="some name", fullname="some fullname")

Wenn ein Objekt jedoch über die ORM Session geladen wird, wird die __init__()-Methode **nicht** aufgerufen.

u1 = session.scalars(select(User).where(User.name == "some name")).first()

Der Grund dafür ist, dass der Vorgang zum Erstellen des Objekts, im obigen Beispiel User, beim Laden aus der Datenbank eher einer **Deserialisierung** ähnelt, wie z. B. Unpickling, anstatt einer anfänglichen Konstruktion. Der Großteil des wichtigen Zustands des Objekts wird nicht zum ersten Mal zusammengesetzt, sondern aus Datenbankzeilen neu geladen.

Daher gibt es, um den Zustand innerhalb des Objekts aufrechtzuerhalten, der nicht Teil der in die Datenbank gespeicherten Daten ist, und der sowohl beim Laden als auch beim Konstruieren von Objekten vorhanden ist, zwei allgemeine Ansätze, die unten beschrieben werden.

  1. Verwenden Sie Python-Deskriptoren wie @property anstelle von Zustand, um Attribute bei Bedarf dynamisch zu berechnen.

    Für einfache Attribute ist dies der einfachste und am wenigsten fehleranfällige Ansatz. Wenn beispielsweise ein Objekt Point mit Point.x und Point.y ein Attribut mit der Summe dieser Attribute wünscht.

    class Point(Base):
        __tablename__ = "point"
        id: Mapped[int] = mapped_column(primary_key=True)
        x: Mapped[int]
        y: Mapped[int]
    
        @property
        def x_plus_y(self):
            return self.x + self.y

    Ein Vorteil der Verwendung dynamischer Deskriptoren ist, dass der Wert jedes Mal neu berechnet wird, was bedeutet, dass er den korrekten Wert beibehält, auch wenn sich die zugrunde liegenden Attribute (in diesem Fall x und y) ändern könnten.

    Andere Formen des obigen Musters umfassen den Dekorator cached_property der Python-Standardbibliothek (der zwischengespeichert und nicht jedes Mal neu berechnet wird) sowie den Dekorator hybrid_property von SQLAlchemy, der Attribute ermöglicht, die auch für SQL-Abfragen funktionieren.

  2. Zustand beim Laden mit InstanceEvents.load() festlegen und optional ergänzende Methoden InstanceEvents.refresh() und InstanceEvents.refresh_flush().

    Dies sind Event-Hooks, die aufgerufen werden, wann immer das Objekt aus der Datenbank geladen oder nach Ablauf erneuert wird. Typischerweise wird nur InstanceEvents.load() benötigt, da nicht gemappter lokaler Objektzustand von Ablaufoperationen nicht betroffen ist. Zur Überarbeitung des obigen Point-Beispiels sieht es so aus:

    from sqlalchemy import event
    
    
    class Point(Base):
        __tablename__ = "point"
        id: Mapped[int] = mapped_column(primary_key=True)
        x: Mapped[int]
        y: Mapped[int]
    
        def __init__(self, x, y, **kw):
            super().__init__(x=x, y=y, **kw)
            self.x_plus_y = x + y
    
    
    @event.listens_for(Point, "load")
    def receive_load(target, context):
        target.x_plus_y = target.x + target.y

    Wenn auch die Refresh-Events verwendet werden, können die Event-Hooks bei Bedarf auf einen einzigen Callable gestapelt werden, wie z. B.:

    @event.listens_for(Point, "load")
    @event.listens_for(Point, "refresh")
    @event.listens_for(Point, "refresh_flush")
    def receive_load(target, context, attrs=None):
        target.x_plus_y = target.x + target.y

    Oben wird das Attribut attrs für die Events refresh und refresh_flush vorhanden sein und eine Liste von Attributnamen angeben, die gerade aktualisiert werden.

Laufzeit-Introspektion von gemappten Klassen, Instanzen und Mappern

Eine Klasse, die mithilfe von registry gemappt wird, verfügt auch über einige Attribute, die für alle Mappings üblich sind:

  • Das Attribut __mapper__ verweist auf den Mapper, der mit der Klasse assoziiert ist.

    mapper = User.__mapper__

    Dieser Mapper ist auch das, was zurückgegeben wird, wenn die Funktion inspect() gegen die gemappte Klasse verwendet wird.

    from sqlalchemy import inspect
    
    mapper = inspect(User)
  • Das Attribut __table__ verweist auf die Table, oder allgemeiner auf das FromClause-Objekt, auf das die Klasse abgebildet wird.

    table = User.__table__

    Dieses FromClause ist auch das, was zurückgegeben wird, wenn das Attribut Mapper.local_table des Mapper verwendet wird.

    table = inspect(User).local_table

    Bei einer Single-Table-Inheritance-Abbildung, bei der eine Klasse eine Unterklasse ist, die keine eigene Tabelle hat, sind das Attribut Mapper.local_table sowie das Attribut .__table__ None. Um das "selektierbare" Objekt abzurufen, das tatsächlich bei einer Abfrage für diese Klasse ausgewählt wird, ist dies über das Attribut Mapper.selectable verfügbar.

    table = inspect(User).selectable

Inspektion von Mapper-Objekten

Wie im vorherigen Abschnitt gezeigt, ist das Mapper-Objekt von jeder abgebildeten Klasse aus zugänglich, unabhängig von der Methode, über das Laufzeitinspektions-API-System. Mit der Funktion inspect() kann man den Mapper von einer abgebildeten Klasse erhalten.

>>> from sqlalchemy import inspect
>>> insp = inspect(User)

Detaillierte Informationen sind verfügbar, einschließlich Mapper.columns.

>>> insp.columns
<sqlalchemy.util._collections.OrderedProperties object at 0x102f407f8>

Dies ist ein Namespace, der in Listenform oder über einzelne Namen angezeigt werden kann.

>>> list(insp.columns)
[Column('id', Integer(), table=<user>, primary_key=True, nullable=False), Column('name', String(length=50), table=<user>), Column('fullname', String(length=50), table=<user>), Column('nickname', String(length=50), table=<user>)]
>>> insp.columns.name
Column('name', String(length=50), table=<user>)

Andere Namespaces umfassen Mapper.all_orm_descriptors, die alle abgebildeten Attribute sowie Hybride und Assoziationsproxys enthält.

>>> insp.all_orm_descriptors
<sqlalchemy.util._collections.ImmutableProperties object at 0x1040e2c68>
>>> insp.all_orm_descriptors.keys()
['fullname', 'nickname', 'name', 'id']

Sowie Mapper.column_attrs.

>>> list(insp.column_attrs)
[<ColumnProperty at 0x10403fde0; id>, <ColumnProperty at 0x10403fce8; name>, <ColumnProperty at 0x1040e9050; fullname>, <ColumnProperty at 0x1040e9148; nickname>]
>>> insp.column_attrs.name
<ColumnProperty at 0x10403fce8; name>
>>> insp.column_attrs.name.expression
Column('name', String(length=50), table=<user>)

Siehe auch

Mapper

Inspektion von abgebildeten Instanzen

Die Funktion inspect() liefert auch Informationen über Instanzen einer abgebildeten Klasse. Wenn sie auf eine Instanz einer abgebildeten Klasse und nicht auf die Klasse selbst angewendet wird, ist das zurückgegebene Objekt als InstanceState bekannt. Dieses Objekt enthält Links sowohl zum Mapper, der von der Klasse verwendet wird, als auch eine detaillierte Schnittstelle, die Informationen über den Zustand einzelner Attribute innerhalb der Instanz liefert, einschließlich ihres aktuellen Werts und wie dieser mit ihrem aus der Datenbank geladenen Wert zusammenhängt.

Gegeben sei eine Instanz der Klasse User, die aus der Datenbank geladen wurde.

>>> u1 = session.scalars(select(User)).first()

Die Funktion inspect() gibt uns ein InstanceState-Objekt zurück.

>>> insp = inspect(u1)
>>> insp
<sqlalchemy.orm.state.InstanceState object at 0x7f07e5fec2e0>

Mit diesem Objekt können wir Elemente wie den Mapper sehen.

>>> insp.mapper
<Mapper at 0x7f07e614ef50; User>

Die Session, an die das Objekt angebunden ist, falls vorhanden.

>>> insp.session
<sqlalchemy.orm.session.Session object at 0x7f07e614f160>

Informationen über den aktuellen Persistenzstatus für das Objekt.

>>> insp.persistent
True
>>> insp.pending
False

Attributstatusinformationen wie Attribute, die noch nicht geladen wurden oder lazy loaded sind (angenommen, addresses bezieht sich auf eine relationship() auf der abgebildeten Klasse zu einer verwandten Klasse).

>>> insp.unloaded
{'addresses'}

Informationen über den aktuellen Status von Attributen in Python, wie z. B. Attribute, die seit dem letzten Flush nicht geändert wurden.

>>> insp.unmodified
{'nickname', 'name', 'fullname', 'id'}

sowie spezifische Historie von Änderungen an Attributen seit dem letzten Flush.

>>> insp.attrs.nickname.value
'nickname'
>>> u1.nickname = "new nickname"
>>> insp.attrs.nickname.history
History(added=['new nickname'], unchanged=(), deleted=['nickname'])