ORM API Features für Abfragen

ORM Ladeoptionen

Ladeoptionen sind Objekte, die, wenn sie an die Methode Select.options() eines Select-Objekts oder ähnliche SQL-Konstrukte übergeben werden, das Laden von spalten- und beziehungsbezogenen Attributen beeinflussen. Die meisten Ladeoptionen erben von der Load-Hierarchie. Eine vollständige Übersicht über die Verwendung von Ladeoptionen finden Sie in den unten verlinkten Abschnitten.

Siehe auch

ORM Ausführungsoptionen

Ausführungsoptionen auf ORM-Ebene sind Schlüsselwortoptionen, die mit einer Anweisungsausführung verknüpft werden können, entweder über den Parameter Session.execute.execution_options, der ein Dictionary-Argument ist, das von Session-Methoden wie Session.execute() und Session.scalars() akzeptiert wird, oder indem sie direkt der auszuführenden Anweisung über die Methode Executable.execution_options() zugeordnet werden, die sie als beliebige Schlüsselwortargumente akzeptiert.

Optionen auf ORM-Ebene unterscheiden sich von den Ausführungsoptionen auf Core-Ebene, die unter Connection.execution_options() dokumentiert sind. Es ist wichtig zu beachten, dass die unten diskutierten ORM-Optionen **nicht** mit den Core-Level-Methoden Connection.execution_options() oder Engine.execution_options() kompatibel sind; die Optionen werden auf dieser Ebene ignoriert, selbst wenn die Engine oder Connection mit der verwendeten Session verknüpft ist.

In diesem Abschnitt wird der Stil der Methode Executable.execution_options() für Beispiele verwendet.

Bestehendes auffüllen

Die Ausführungsoption populate_existing stellt sicher, dass für alle geladenen Zeilen die entsprechenden Instanzen in der Session vollständig aktualisiert werden – alle vorhandenen Daten in den Objekten (einschließlich ausstehender Änderungen) werden gelöscht und durch die aus dem Ergebnis geladenen Daten ersetzt.

Die beispielhafte Verwendung sieht so aus

>>> stmt = select(User).execution_options(populate_existing=True)
>>> result = session.execute(stmt)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account ...

Normalerweise werden ORM-Objekte nur einmal geladen, und wenn sie in einer nachfolgenden Ergebniszeile mit dem Primärschlüssel übereinstimmen, wird die Zeile nicht auf das Objekt angewendet. Dies dient sowohl dem Schutz ausstehender, noch nicht gefluschter Änderungen am Objekt als auch der Vermeidung des Overheads und der Komplexität der Aktualisierung bereits vorhandener Daten. Die Session geht von einem Standardarbeitsmodell einer hochisolierten Transaktion aus, und in dem Maße, in dem Daten innerhalb der Transaktion außerhalb der lokalen Änderungen von Änderungen betroffen sind, würden diese Anwendungsfälle mit expliziten Schritten wie dieser Methode behandelt.

Mit populate_existing kann jede Gruppe von Objekten, die mit einer Abfrage übereinstimmen, aktualisiert werden, und es ermöglicht auch die Steuerung von Beziehungs-Ladeoptionen. Z.B. um eine Instanz zu aktualisieren und gleichzeitig eine zugehörige Gruppe von Objekten zu aktualisieren

stmt = (
    select(User)
    .where(User.name.in_(names))
    .execution_options(populate_existing=True)
    .options(selectinload(User.addresses))
)
# will refresh all matching User objects as well as the related
# Address objects
users = session.execute(stmt).scalars().all()

Ein weiterer Anwendungsfall für populate_existing ist die Unterstützung verschiedener Attributladefunktionen, die beeinflussen können, wie ein Attribut pro Abfrage geladen wird. Optionen, für die dies gilt, sind

Die Ausführungsoption populate_existing ist äquivalent zur Methode Query.populate_existing() in ORM-Abfragen im 1.x-Stil.

Autoflush

Diese Option wird, wenn sie als False übergeben wird, dazu führen, dass die Session den "autoflush"-Schritt nicht ausführt. Sie ist äquivalent zur Verwendung des Kontextmanagers Session.no_autoflush, um autoflush zu deaktivieren.

>>> stmt = select(User).execution_options(autoflush=False)
>>> session.execute(stmt)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account ...

Diese Option funktioniert auch bei ORM-aktivierten Update- und Delete-Abfragen.

Die Ausführungsoption autoflush ist äquivalent zur Methode Query.autoflush() in ORM-Abfragen im 1.x-Stil.

Siehe auch

Flushing

Abrufen großer Ergebnissets mit Yield Per

Die Ausführungsoption yield_per ist ein Integerwert, der dazu führt, dass das Result nur eine begrenzte Anzahl von Zeilen und/oder ORM-Objekten auf einmal puffert, bevor Daten für den Client verfügbar gemacht werden.

Normalerweise ruft die ORM **alle** Zeilen sofort ab, erstellt für jede ORM-Objekte und fasst diese Objekte in einem einzigen Puffer zusammen, bevor dieser Puffer dem Result-Objekt als Quelle für zurückzugebende Zeilen übergeben wird. Die Begründung für dieses Verhalten ist, korrekte Ergebnisse für Funktionen wie Joined Eager Loading, Entitäten-Unifizierung und die allgemeine Verarbeitung von Ergebnissen zu ermöglichen, die auf der Identitätszuordnung basieren, um einen konsistenten Zustand für jedes Objekt in einem Ergebnisset zu gewährleisten, während es abgerufen wird.

Der Zweck der Option yield_per ist es, dieses Verhalten zu ändern, sodass das ORM-Ergebnisset für die Iteration durch sehr große Ergebnissets (z. B. > 10.000 Zeilen) optimiert ist, bei denen der Benutzer festgestellt hat, dass die oben genannten Muster nicht zutreffen. Wenn yield_per verwendet wird, werden die ORM-Ergebnisse in Teil-Kollektionen gebatcht und Zeilen aus jeder Teil-Kollektion einzeln geliefert, während das Result-Objekt iteriert wird, sodass der Python-Interpreter keine sehr großen Speicherbereiche deklarieren muss, was sowohl zeitaufwendig ist als auch zu übermäßiger Speichernutzung führt. Die Option beeinflusst sowohl die Art und Weise, wie der Datenbankcursor verwendet wird, als auch wie die ORM Zeilen und Objekte erstellt, die an das Result übergeben werden.

Tipp

Aus dem Obigen ergibt sich, dass das Result iterativ verarbeitet werden muss, d. h. durch Iteration wie for row in result oder durch die Verwendung von partiellen Zeilenmethoden wie Result.fetchmany() oder Result.partitions(). Das Aufrufen von Result.all() untergräbt den Zweck der Verwendung von yield_per.

Die Verwendung von yield_per ist äquivalent zur Verwendung der Ausführungsoption Connection.execution_options.stream_results, die serverseitige Cursors für das Backend auswählt, falls unterstützt, und der Methode Result.yield_per() des zurückgegebenen Result-Objekts, die eine feste Anzahl von Zeilen festlegt, die abgerufen werden sollen, sowie eine entsprechende Begrenzung für die Anzahl der gleichzeitig zu erstellenden ORM-Objekte.

Tipp

yield_per ist jetzt auch als Ausführungsoption auf Core-Ebene verfügbar, wie ausführlich unter Verwendung von Server Side Cursors (auch Stream Results genannt) beschrieben. Dieser Abschnitt beschreibt die Verwendung von yield_per als Ausführungsoption mit einer ORM- Session. Die Option verhält sich in beiden Kontexten so ähnlich wie möglich.

Bei Verwendung mit der ORM muss yield_per entweder über die Methode Executable.execution_options() auf der gegebenen Anweisung oder durch Übergabe an den Parameter Session.execute.execution_options von Session.execute() oder anderen ähnlichen Session-Methoden wie Session.scalars() festgelegt werden. Die typische Verwendung für das Abrufen von ORM-Objekten wird unten gezeigt.

>>> stmt = select(User).execution_options(yield_per=10)
>>> for user_obj in session.scalars(stmt):
...     print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

Der obige Code ist äquivalent zu dem unten stehenden Beispiel, das Connection.execution_options.stream_results und Connection.execution_options.max_row_buffer Core-Level Ausführungsoptionen in Verbindung mit der Methode Result.yield_per() von Result verwendet.

# equivalent code
>>> stmt = select(User).execution_options(stream_results=True, max_row_buffer=10)
>>> for user_obj in session.scalars(stmt).yield_per(10):
...     print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

yield_per wird auch häufig in Kombination mit der Methode Result.partitions() verwendet, die Zeilen in gruppierten Partitionen iteriert. Die Größe jeder Partition entspricht standardmäßig dem Integerwert, der an yield_per übergeben wird, wie im folgenden Beispiel gezeigt.

>>> stmt = select(User).execution_options(yield_per=10)
>>> for partition in session.scalars(stmt).partitions():
...     for user_obj in partition:
...         print(user_obj)
SELECT user_account.id, user_account.name, user_account.fullname FROM user_account [...] ()
User(id=1, name='spongebob', fullname='Spongebob Squarepants') User(id=2, name='sandy', fullname='Sandy Cheeks') ... >>> # ... rows continue ...

Die Ausführungsoption yield_per ist **nicht kompatibel** mit dem "Subquery" Eager Loading oder dem "Joined" Eager Loading bei Verwendung von Sammlungen. Sie ist möglicherweise kompatibel mit "Select in" Eager Loading, vorausgesetzt, der Treiber unterstützt mehrere unabhängige Cursors.

Zusätzlich ist die Ausführungsoption yield_per nicht kompatibel mit der Methode Result.unique(); da diese Methode darauf basiert, einen vollständigen Satz von Identitäten für alle Zeilen zu speichern, würde sie notwendigerweise den Zweck der Verwendung von yield_per untergraben, nämlich die Verarbeitung einer beliebig großen Anzahl von Zeilen.

Geändert in Version 1.4.6: Es wird eine Ausnahme ausgelöst, wenn ORM-Zeilen aus einem Result-Objekt abgerufen werden, das die Filterfunktion Result.unique() verwendet, während gleichzeitig die Ausführungsoption yield_per verwendet wird.

Bei Verwendung des Legacy- Query-Objekts mit ORM-Verwendung im 1.x-Stil hat die Methode Query.yield_per() das gleiche Ergebnis wie die Ausführungsoption yield_per.

Identitätstoken

Deep Alchemy

Diese Option ist eine Funktion für fortgeschrittene Anwendungsfälle und ist hauptsächlich für die Verwendung mit der Erweiterung Horizontal Sharding gedacht. Für typische Fälle des Ladens von Objekten mit identischen Primärschlüsseln aus verschiedenen "Shards" oder Partitionen sollten Sie zuerst einzelne Session-Objekte pro Shard verwenden.

Das "Identitätstoken" ist ein beliebiger Wert, der innerhalb des Identitätsschlüssels von neu geladenen Objekten assoziiert werden kann. Dieses Element existiert in erster Linie zur Unterstützung von Erweiterungen, die zeilenweises "Sharding" durchführen, bei dem Objekte aus beliebigen Replikaten einer bestimmten Datenbanktabelle geladen werden können, die jedoch überlappende Primärschlüsselwerte haben. Der Hauptverbraucher von "Identitätstoken" ist die Erweiterung Horizontal Sharding, die ein allgemeines Framework für die Persistenz von Objekten in mehreren "Shards" einer bestimmten Datenbanktabelle bietet.

Die Ausführungsoption identity_token kann pro Abfrage verwendet werden, um dieses Token direkt zu beeinflussen. Durch direkte Verwendung können Sie eine Session mit mehreren Instanzen eines Objekts füllen, die den gleichen Primärschlüssel und die gleiche Quelltabelle haben, aber unterschiedliche "Identitäten".

Ein solches Beispiel ist das Füllen einer Session mit Objekten, die aus Tabellen mit demselben Namen in verschiedenen Schemas stammen, unter Verwendung der Funktion zur Übersetzung von Schemanamen, die die Wahl des Schemas im Geltungsbereich von Abfragen beeinflussen kann. Gegeben sei eine Zuordnung wie

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


class Base(DeclarativeBase):
    pass


class MyTable(Base):
    __tablename__ = "my_table"

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

Der Standard-"Schema"-Name für die obige Klasse ist None, was bedeutet, dass keine Schema-Qualifizierung in SQL-Anweisungen geschrieben wird. Wenn wir jedoch Connection.execution_options.schema_translate_map verwenden und None auf ein alternatives Schema abbilden, können wir Instanzen von MyTable in zwei verschiedene Schemas platzieren.

engine = create_engine(
    "postgresql+psycopg://scott:tiger@localhost/test",
)

with Session(
    engine.execution_options(schema_translate_map={None: "test_schema"})
) as sess:
    sess.add(MyTable(name="this is schema one"))
    sess.commit()

with Session(
    engine.execution_options(schema_translate_map={None: "test_schema_2"})
) as sess:
    sess.add(MyTable(name="this is schema two"))
    sess.commit()

Die beiden obigen Blöcke erstellen jeweils ein Session-Objekt, das mit einer anderen Schema-Übersetzungskarte verknüpft ist, und eine Instanz von MyTable wird sowohl in test_schema.my_table als auch in test_schema_2.my_table gespeichert.

Die obigen Session-Objekte sind unabhängig. Wenn wir beide Objekte in einer Transaktion speichern wollten, müssten wir die Erweiterung Horizontal Sharding dafür verwenden.

Wir können jedoch das Abfragen dieser Objekte in einer einzigen Session wie folgt veranschaulichen.

with Session(engine) as sess:
    obj1 = sess.scalar(
        select(MyTable)
        .where(MyTable.id == 1)
        .execution_options(
            schema_translate_map={None: "test_schema"},
            identity_token="test_schema",
        )
    )
    obj2 = sess.scalar(
        select(MyTable)
        .where(MyTable.id == 1)
        .execution_options(
            schema_translate_map={None: "test_schema_2"},
            identity_token="test_schema_2",
        )
    )

Sowohl obj1 als auch obj2 sind voneinander verschieden. Sie beziehen sich jedoch beide auf den Primärschlüssel-ID 1 für die Klasse MyTable und sind dennoch unterschiedlich. So kommt das identity_token ins Spiel, was wir bei der Inspektion jedes Objekts sehen können, indem wir InstanceState.key betrachten, um die beiden unterschiedlichen Identitätstoken anzuzeigen.

>>> from sqlalchemy import inspect
>>> inspect(obj1).key
(<class '__main__.MyTable'>, (1,), 'test_schema')
>>> inspect(obj2).key
(<class '__main__.MyTable'>, (1,), 'test_schema_2')

Die obige Logik findet automatisch statt, wenn die Erweiterung Horizontal Sharding verwendet wird.

Neu in Version 2.0.0rc1: - hinzugefügt die ORM-Ausführungsoption identity_token.

Siehe auch

Horizontal Sharding - im Abschnitt ORM Examples. Siehe das Skript separate_schema_translates.py für eine Demonstration des obigen Anwendungsfalls mit der vollständigen Sharding-API.

Entitäten und Spalten aus ORM-aktivierten SELECT- und DML-Anweisungen inspizieren

Das Konstrukt select() sowie die Konstrukte insert(), update() und delete() (für letztere DML-Konstrukte, ab SQLAlchemy 1.4.33) unterstützen die Fähigkeit, die Entitäten zu inspizieren, gegen die diese Anweisungen erstellt werden, sowie die Spalten und Datentypen, die in einem Ergebnisset zurückgegeben würden.

Für ein Select-Objekt sind diese Informationen über das Attribut Select.column_descriptions verfügbar. Dieses Attribut funktioniert genauso wie das Legacy-Attribut Query.column_descriptions. Das zurückgegebene Format ist eine Liste von Dictionaries.

>>> from pprint import pprint
>>> user_alias = aliased(User, name="user2")
>>> stmt = select(User, User.id, user_alias)
>>> pprint(stmt.column_descriptions)
[{'aliased': False,
  'entity': <class 'User'>,
  'expr': <class 'User'>,
  'name': 'User',
  'type': <class 'User'>},
 {'aliased': False,
  'entity': <class 'User'>,
  'expr': <....InstrumentedAttribute object at ...>,
  'name': 'id',
  'type': Integer()},
 {'aliased': True,
  'entity': <AliasedClass ...; User>,
  'expr': <AliasedClass ...; User>,
  'name': 'user2',
  'type': <class 'User'>}]

Wenn Select.column_descriptions mit Nicht-ORM-Objekten wie einfachen Table- oder Column-Objekten verwendet wird, enthalten die Einträge grundlegende Informationen über einzelne Spalten, die in allen Fällen zurückgegeben werden.

>>> stmt = select(user_table, address_table.c.id)
>>> pprint(stmt.column_descriptions)
[{'expr': Column('id', Integer(), table=<user_account>, primary_key=True, nullable=False),
  'name': 'id',
  'type': Integer()},
 {'expr': Column('name', String(), table=<user_account>, nullable=False),
  'name': 'name',
  'type': String()},
 {'expr': Column('fullname', String(), table=<user_account>),
  'name': 'fullname',
  'type': String()},
 {'expr': Column('id', Integer(), table=<address>, primary_key=True, nullable=False),
  'name': 'id_1',
  'type': Integer()}]

Geändert in Version 1.4.33: Das Attribut Select.column_descriptions gibt jetzt einen Wert zurück, wenn es gegen ein Select verwendet wird, das nicht ORM-fähig ist. Zuvor würde dies NotImplementedError auslösen.

Für insert(), update() und delete() Konstrukte gibt es zwei separate Attribute. Eines ist UpdateBase.entity_description, das Informationen über die primäre ORM-Entität und die Datenbanktabelle zurückgibt, die das DML-Konstrukt beeinflussen würde

>>> from sqlalchemy import update
>>> stmt = update(User).values(name="somename").returning(User.id)
>>> pprint(stmt.entity_description)
{'entity': <class 'User'>,
 'expr': <class 'User'>,
 'name': 'User',
 'table': Table('user_account', ...),
 'type': <class 'User'>}

Tipp

Die UpdateBase.entity_description enthält einen Eintrag "table", der tatsächlich die Tabelle ist, in die eingefügt, aktualisiert oder aus der gelöscht werden soll, was nicht immer dasselbe ist wie das SQL-“selektierbare”, auf das die Klasse abgebildet werden kann. In einem Joined-Table-Inheritance-Szenario bezieht sich "table" beispielsweise auf die lokale Tabelle für die gegebene Entität.

Das andere ist UpdateBase.returning_column_descriptions, das Informationen über die Spalten in der RETURNING-Sammlung liefert, in einer Weise, die grob der von Select.column_descriptions ähnelt.

>>> pprint(stmt.returning_column_descriptions)
[{'aliased': False,
  'entity': <class 'User'>,
  'expr': <sqlalchemy.orm.attributes.InstrumentedAttribute ...>,
  'name': 'id',
  'type': Integer()}]

Neu in Version 1.4.33: Attribute UpdateBase.entity_description und UpdateBase.returning_column_descriptions hinzugefügt.

Zusätzliche ORM-API-Konstrukte

Objektname Beschreibung

aliased(element[, alias, name, flat, ...])

Erzeugt einen Alias für das gegebene Element, normalerweise eine AliasedClass-Instanz.

AliasedClass

Repräsentiert eine "aliassierte" Form einer abgebildeten Klasse zur Verwendung mit Query.

AliasedInsp

Bietet eine Inspektionsschnittstelle für ein AliasedClass-Objekt.

Bundle

Eine Gruppierung von SQL-Ausdrücken, die von einer Query unter einem Namespace zurückgegeben werden.

join(left, right[, onclause, isouter, ...])

Erzeugt einen Inner Join zwischen linken und rechten Klauseln.

outerjoin(left, right[, onclause, full])

Erzeugt einen Left Outer Join zwischen linken und rechten Klauseln.

with_loader_criteria(entity_or_base, where_criteria[, loader_only, include_aliases, ...])

Fügt der Ladung für alle Vorkommen einer bestimmten Entität zusätzliche WHERE-Kriterien hinzu.

with_parent(instance, prop[, from_entity])

Erstellt ein Filterkriterium, das die primäre Entität dieser Abfrage mit der gegebenen verwandten Instanz über die konfigurierte relationship()-Konfiguration verbindet.

function sqlalchemy.orm.aliased(element: _EntityType[_O] | FromClause, alias: FromClause | None = None, name: str | None = None, flat: bool = False, adapt_on_names: bool = False) AliasedClass[_O] | FromClause | AliasedType[_O]

Erzeugt einen Alias für das gegebene Element, normalerweise eine AliasedClass-Instanz.

Z. B.

my_alias = aliased(MyClass)

stmt = select(MyClass, my_alias).filter(MyClass.id > my_alias.id)
result = session.execute(stmt)

Die Funktion aliased() wird verwendet, um eine Ad-hoc-Abbildung einer abgebildeten Klasse auf ein neues auswählbares Element zu erstellen. Standardmäßig wird ein auswählbares Element aus dem normalerweise abgebildeten auswählbaren Element (typischerweise eine Table) mithilfe der Methode FromClause.alias() generiert. aliased() kann jedoch auch verwendet werden, um die Klasse mit einer neuen select()-Anweisung zu verknüpfen. Außerdem ist die Funktion with_polymorphic() eine Variante von aliased(), die dazu dient, ein sogenanntes "polymorphes auswählbares" anzugeben, das die Vereinigung mehrerer geerbter Unterklassen gleichzeitig darstellt.

Der Einfachheit halber akzeptiert die Funktion aliased() auch einfache FromClause-Konstrukte wie eine Table oder select()-Konstrukt. In diesen Fällen wird die Methode FromClause.alias() auf dem Objekt aufgerufen und das neue Alias-Objekt zurückgegeben. Das zurückgegebene Alias ist in diesem Fall nicht ORM-abgebildet.

Parameter:
  • element – das abzubildende Element. Ist normalerweise eine abgebildete Klasse, kann aber aus Bequemlichkeitsgründen auch ein FromClause-Element sein.

  • alias – Optionales auswählbares Element, auf das das Element abgebildet werden soll. Dies wird normalerweise verwendet, um das Objekt mit einer Unterabfrage zu verknüpfen, und sollte ein aliassiertes Select-Konstrukt sein, wie es aus der Methode Query.subquery() oder den Methoden Select.subquery() oder Select.alias() des select()-Konstrukts erzeugt werden würde.

  • name – Optionaler Zeichenkettenname für den Alias, falls nicht durch den Parameter alias angegeben. Der Name ist unter anderem der Attributname, der über Tupel erreichbar ist, die von einem Query-Objekt zurückgegeben werden. Nicht unterstützt bei der Erstellung von Aliasen von Join-Objekten.

  • flat

    Boolean, wird an den Aufruf von FromClause.alias() weitergegeben, sodass Aliase von Join-Objekten die einzelnen Tabellen innerhalb des Joins aliassieren, anstatt eine Unterabfrage zu erstellen. Dies wird von allen modernen Datenbanken im Hinblick auf rechts verschachtelte Joins im Allgemeinen unterstützt und führt im Allgemeinen zu effizienteren Abfragen.

    Wenn aliased.flat mit aliased.name kombiniert wird, benennen die resultierenden Joins einzelne Tabellen mit einem Benennungsschema ähnlich wie <prefix>_<tablename>. Dieses Benennungsschema dient nur der Sichtbarkeit/Fehlersuche und das spezifische Schema kann ohne Vorankündigung geändert werden.

    Neu in Version 2.0.32: Unterstützung für die Kombination von aliased.name mit aliased.flat hinzugefügt. Zuvor wurde hierbei NotImplementedError ausgelöst.

  • adapt_on_names

    Wenn True, wird eine liberalere "Übereinstimmung" verwendet, wenn die abgebildeten Spalten der ORM-Entität mit denen des gegebenen auswählbaren Elements abgeglichen werden – ein namensbasierter Abgleich wird durchgeführt, wenn das gegebene auswählbare Element keine Spalte hat, die einer der Entität entspricht. Der Anwendungsfall dafür ist die Verknüpfung einer Entität mit einem abgeleiteten auswählbaren Element, wie z. B. einem, das Aggregatfunktionen verwendet.

    class UnitPrice(Base):
        __tablename__ = "unit_price"
        ...
        unit_id = Column(Integer)
        price = Column(Numeric)
    
    
    aggregated_unit_price = (
        Session.query(func.sum(UnitPrice.price).label("price"))
        .group_by(UnitPrice.unit_id)
        .subquery()
    )
    
    aggregated_unit_price = aliased(
        UnitPrice, alias=aggregated_unit_price, adapt_on_names=True
    )

    Oben beziehen sich Funktionen auf aggregated_unit_price, die sich auf .price beziehen, auf die Spalte func.sum(UnitPrice.price).label('price'), da sie nach dem Namen "price" abgeglichen wird. Normalerweise hätte die Funktion "price" keine "Spaltenentsprechung" zur tatsächlichen Spalte UnitPrice.price, da sie keine Proxy der Originalspalte ist.

class sqlalchemy.orm.util.AliasedClass

Repräsentiert eine "aliassierte" Form einer abgebildeten Klasse zur Verwendung mit Query.

Das ORM-Äquivalent eines alias()-Konstrukts, dieses Objekt ahmt die abgebildete Klasse mithilfe eines __getattr__-Schemas nach und behält einen Verweis auf ein echtes Alias-Objekt.

Ein Hauptzweck von AliasedClass ist es, als Alternative innerhalb einer von der ORM generierten SQL-Anweisung zu dienen, sodass eine vorhandene abgebildete Entität in mehreren Kontexten verwendet werden kann. Ein einfaches Beispiel

# find all pairs of users with the same name
user_alias = aliased(User)
session.query(User, user_alias).join(
    (user_alias, User.id > user_alias.id)
).filter(User.name == user_alias.name)

AliasedClass kann auch eine vorhandene abgebildete Klasse auf ein völlig neues auswählbares Element abbilden, vorausgesetzt, dieses auswählbare Element ist spaltenkompatibel mit dem vorhandenen abgebildeten auswählbaren Element, und es kann auch in einer Abbildung als Ziel einer relationship() konfiguriert werden. Beispiele finden Sie unter den nachfolgenden Links.

Das Objekt AliasedClass wird typischerweise mithilfe der Funktion aliased() konstruiert. Es wird auch mit zusätzlicher Konfiguration erzeugt, wenn die Funktion with_polymorphic() verwendet wird.

Das resultierende Objekt ist eine Instanz von AliasedClass. Dieses Objekt implementiert ein Attributschema, das dieselbe Attribut- und Methodenschnittstelle wie die ursprüngliche abgebildete Klasse erzeugt, wodurch AliasedClass mit jeder Attributtechnik kompatibel ist, die auf der ursprünglichen Klasse funktioniert, einschließlich Hybrid-Attributen (siehe Hybrid Attributes).

Die AliasedClass kann mithilfe von inspect() auf ihren zugrunde liegenden Mapper, aliassierten auswählbaren Elementen und anderen Informationen geprüft werden.

from sqlalchemy import inspect

my_alias = aliased(MyClass)
insp = inspect(my_alias)

Das resultierende Inspektionsobjekt ist eine Instanz von AliasedInsp.

Klassensignatur

class sqlalchemy.orm.AliasedClass (sqlalchemy.inspection.Inspectable, sqlalchemy.orm.ORMColumnsClauseRole)

class sqlalchemy.orm.util.AliasedInsp

Bietet eine Inspektionsschnittstelle für ein AliasedClass-Objekt.

Das Objekt AliasedInsp wird zurückgegeben, wenn ein AliasedClass mithilfe der Funktion inspect() übergeben wird.

from sqlalchemy import inspect
from sqlalchemy.orm import aliased

my_alias = aliased(MyMappedClass)
insp = inspect(my_alias)

Attribute von AliasedInsp umfassen

  • entity - die dargestellte AliasedClass.

  • mapper - der Mapper, der die zugrunde liegende Klasse abbildet.

  • selectable - das Alias-Konstrukt, das letztendlich eine aliassierte Table oder ein Select-Konstrukt darstellt.

  • name - der Name des Alias. Wird auch als Attributname verwendet, wenn er in einem Ergebnis-Tupel von Query zurückgegeben wird.

  • with_polymorphic_mappers - Sammlung von Mapper-Objekten, die alle Mapper angeben, die im Select-Konstrukt für die AliasedClass ausgedrückt werden.

  • polymorphic_on - eine alternative Spalte oder ein SQL-Ausdruck, der als "Diskriminator" für eine polymorphe Ladung verwendet wird.

Klassensignatur

class sqlalchemy.orm.AliasedInsp (sqlalchemy.orm.ORMEntityColumnsClauseRole, sqlalchemy.orm.ORMFromClauseRole, sqlalchemy.sql.cache_key.HasCacheKey, sqlalchemy.orm.base.InspectionAttr, sqlalchemy.util.langhelpers.MemoizedSlots, sqlalchemy.inspection.Inspectable, typing.Generic)

class sqlalchemy.orm.Bundle

Eine Gruppierung von SQL-Ausdrücken, die von einer Query unter einem Namespace zurückgegeben werden.

Das Bundle erlaubt im Wesentlichen das Verschachteln von Tupel-basierten Ergebnissen, die von einem spaltenorientierten Query-Objekt zurückgegeben werden. Es ist auch durch einfaches Unterklassieren erweiterbar, wobei die primäre Möglichkeit zur Überschreibung darin besteht, wie die Menge der Ausdrücke zurückgegeben werden soll, was Post-Processing sowie benutzerdefinierte Rückgabetypen ermöglicht, ohne ORM-identitätsabgebildete Klassen einzubeziehen.

Klassensignatur

class sqlalchemy.orm.Bundle (sqlalchemy.orm.ORMColumnsClauseRole, sqlalchemy.sql.annotation.SupportsCloneAnnotations, sqlalchemy.sql.cache_key.MemoizedHasCacheKey, sqlalchemy.inspection.Inspectable, sqlalchemy.orm.base.InspectionAttr)

method sqlalchemy.orm.Bundle.__init__(name: str, *exprs: _ColumnExpressionArgument[Any], **kw: Any)

Konstruiert ein neues Bundle.

z. B.

bn = Bundle("mybundle", MyClass.x, MyClass.y)

for row in session.query(bn).filter(bn.c.x == 5).filter(bn.c.y == 4):
    print(row.mybundle.x, row.mybundle.y)
Parameter:
  • name – Name des Bundles.

  • *exprs – Spalten oder SQL-Ausdrücke, aus denen das Bundle besteht.

  • single_entity=False – Wenn True, können Zeilen für dieses Bundle als "Einzelne Entität" außerhalb eines umschließenden Tupels zurückgegeben werden, genauso wie eine abgebildete Entität.

attribute sqlalchemy.orm.Bundle.c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

Ein Alias für Bundle.columns.

attribute sqlalchemy.orm.Bundle.columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]

Ein Namespace von SQL-Ausdrücken, auf die sich dieses Bundle bezieht.

z. B.

bn = Bundle("mybundle", MyClass.x, MyClass.y)

q = sess.query(bn).filter(bn.c.x == 5)

Verschachtelung von Bundles wird ebenfalls unterstützt

b1 = Bundle(
    "b1",
    Bundle("b2", MyClass.a, MyClass.b),
    Bundle("b3", MyClass.x, MyClass.y),
)

q = sess.query(b1).filter(b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9)

Siehe auch

Bundle.c

methode sqlalchemy.orm.Bundle.create_row_processor(query: Select[Any], procs: Sequence[Callable[[Row[Any]], Any]], labels: Sequence[str]) Callable[[Row[Any]], Any]

Erzeugt die „Zeilenverarbeitungs“-Funktion für dieses Bundle.

Kann von Unterklassen überschrieben werden, um benutzerdefinierte Verhaltensweisen bei abgerufenen Ergebnissen bereitzustellen. Die Methode erhält zur Abfragezeit das Anweisungsobjekt und eine Menge von „Zeilenprozessor“-Funktionen; diese Prozessorfunktionen geben bei einer Ergebniszeile den einzelnen Attributwert zurück, der dann in jede beliebige Rückgabedatenstruktur angepasst werden kann.

Das folgende Beispiel veranschaulicht das Ersetzen der üblichen Row-Rückgabestruktur durch ein direktes Python-Dictionary

from sqlalchemy.orm import Bundle


class DictBundle(Bundle):
    def create_row_processor(self, query, procs, labels):
        "Override create_row_processor to return values as dictionaries"

        def proc(row):
            return dict(zip(labels, (proc(row) for proc in procs)))

        return proc

Ein Ergebnis aus dem obigen Bundle gibt Dictionary-Werte zurück

bn = DictBundle("mybundle", MyClass.data1, MyClass.data2)
for row in session.execute(select(bn)).where(bn.c.data1 == "d1"):
    print(row.mybundle["data1"], row.mybundle["data2"])
attribut sqlalchemy.orm.Bundle.is_aliased_class = False

True, wenn dieses Objekt eine Instanz von AliasedClass ist.

attribut sqlalchemy.orm.Bundle.is_bundle = True

True, wenn dieses Objekt eine Instanz von Bundle ist.

attribut sqlalchemy.orm.Bundle.is_clause_element = False

True, wenn dieses Objekt eine Instanz von ClauseElement ist.

attribut sqlalchemy.orm.Bundle.is_mapper = False

True, wenn dieses Objekt eine Instanz von Mapper ist.

methode sqlalchemy.orm.Bundle.label(name)

Bietet eine Kopie dieses Bundle mit einem neuen Label.

attribut sqlalchemy.orm.Bundle.single_entity = False

Wenn True, werden Abfragen nach einem einzelnen Bundle als einzelne Entität zurückgegeben, anstatt als Element in einem Schlüssel-Tupel.

funktion sqlalchemy.orm.with_loader_criteria(entity_or_base: _EntityType[Any], where_criteria: _ColumnExpressionArgument[bool] | Callable[[Any], _ColumnExpressionArgument[bool]], loader_only: bool = False, include_aliases: bool = False, propagate_to_loaders: bool = True, track_closure_variables: bool = True) LoaderCriteriaOption

Fügt der Ladung für alle Vorkommen einer bestimmten Entität zusätzliche WHERE-Kriterien hinzu.

Neu in Version 1.4.

Die Option with_loader_criteria() dient dazu, bestimmte Kriterien für eine bestimmte Art von Entität in einer Abfrage hinzuzufügen, und zwar **global**, d. h. sie gilt für die Entität, wie sie in der SELECT-Abfrage sowie in allen Unterabfragen, Join-Bedingungen und Beziehungs-Loads erscheint, einschließlich sowohl von Eager- als auch von Lazy-Loadern, ohne dass sie in einem bestimmten Teil der Abfrage angegeben werden muss. Die Rendering-Logik verwendet dasselbe System wie Single Table Inheritance, um sicherzustellen, dass ein bestimmter Diskriminator einer Tabelle zugeordnet wird.

Beispielsweise können wir mit Abfragen im „2.0-Stil“ die Art und Weise einschränken, wie die Sammlung User.addresses geladen wird, unabhängig von der Art des verwendeten Ladens.

from sqlalchemy.orm import with_loader_criteria

stmt = select(User).options(
    selectinload(User.addresses),
    with_loader_criteria(Address, Address.email_address != "foo"),
)

Oben wendet der „selectinload“ für User.addresses die gegebenen Filterkriterien auf die WHERE-Klausel an.

Ein weiteres Beispiel, bei dem die Filterung auf die ON-Klausel des Joins angewendet wird, in diesem Beispiel mit Abfragen im „1.x-Stil“.

q = (
    session.query(User)
    .outerjoin(User.addresses)
    .options(with_loader_criteria(Address, Address.email_address != "foo"))
)

Der Hauptzweck von with_loader_criteria() ist die Verwendung im SessionEvents.do_orm_execute() Event-Handler, um sicherzustellen, dass alle Vorkommen einer bestimmten Entität auf eine bestimmte Weise gefiltert werden, z. B. zur Zugriffssteuerungsrolle. Es kann auch verwendet werden, um Kriterien für Beziehungs-Loads anzuwenden. Im folgenden Beispiel können wir bestimmte Regeln auf alle Abfragen anwenden, die von einer bestimmten Session ausgegeben werden.

session = Session(bind=engine)


@event.listens_for("do_orm_execute", session)
def _add_filtering_criteria(execute_state):

    if (
        execute_state.is_select
        and not execute_state.is_column_load
        and not execute_state.is_relationship_load
    ):
        execute_state.statement = execute_state.statement.options(
            with_loader_criteria(
                SecurityRole,
                lambda cls: cls.role.in_(["some_role"]),
                include_aliases=True,
            )
        )

Im obigen Beispiel fängt das Ereignis SessionEvents.do_orm_execute() alle mit der Session ausgegebenen Abfragen ab. Für Abfragen, die SELECT-Anweisungen sind und keine Attribut- oder Beziehungs-Loads sind, wird der Abfrage eine benutzerdefinierte Option with_loader_criteria() hinzugefügt. Die Option with_loader_criteria() wird in der gegebenen Anweisung verwendet und auch automatisch an alle von dieser Abfrage abgeleiteten Beziehungs-Loads weitergegeben.

Das als Kriterium übergebene Argument ist ein lambda, das ein cls-Argument akzeptiert. Die gegebene Klasse wird erweitert, um alle zugeordneten Unterklassen einzuschließen, und muss selbst keine zugeordnete Klasse sein.

Tipp

Bei der Verwendung der Option with_loader_criteria() in Verbindung mit der Ladeoption contains_eager() ist zu beachten, dass with_loader_criteria() nur den Teil der Abfrage beeinflusst, der bestimmt, welche SQL-Befehle in Bezug auf die WHERE- und FROM-Klauseln gerendert werden. Die Option contains_eager() beeinflusst nicht das Rendering der SELECT-Anweisung außerhalb der Spaltenklausel und hat daher keine Wechselwirkung mit der Option with_loader_criteria(). Die Funktionsweise von contains_eager() ist jedoch dafür gedacht, mit einer Abfrage verwendet zu werden, die bereits einige der zusätzlichen Entitäten auswählt, wobei with_loader_criteria() ihre zusätzlichen Kriterien anwenden kann.

Im folgenden Beispiel, unter der Annahme einer Zuordnungsbeziehung als A -> A.bs -> B, beeinflusst die gegebene Option with_loader_criteria() die Art und Weise, wie der JOIN gerendert wird.

stmt = (
    select(A)
    .join(A.bs)
    .options(contains_eager(A.bs), with_loader_criteria(B, B.flag == 1))
)

Oben beeinflusst die gegebene Option with_loader_criteria() die ON-Klausel des Joins, der durch .join(A.bs) angegeben wird, also wird er wie erwartet angewendet. Die Option contains_eager() bewirkt, dass Spalten von B zur Spaltenklausel hinzugefügt werden.

SELECT
    b.id, b.a_id, b.data, b.flag,
    a.id AS id_1,
    a.data AS data_1
FROM a JOIN b ON a.id = b.a_id AND b.flag = :flag_1

Die Verwendung der Option contains_eager() innerhalb der obigen Anweisung hat keine Auswirkung auf das Verhalten der Option with_loader_criteria(). Wenn die Option contains_eager() weggelassen würde, wäre die SQL-Anweisung in Bezug auf die FROM- und WHERE-Klauseln dieselbe, wobei with_loader_criteria() weiterhin seine Kriterien zur ON-Klausel des Joins hinzufügt. Die Hinzufügung von contains_eager() beeinflusst nur die Spaltenklausel, indem zusätzliche Spalten zu b hinzugefügt werden, die dann vom ORM verarbeitet werden, um B-Instanzen zu erzeugen.

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 Erzielung erheblicher Geschwindigkeitssteigerungen bei der Erzeugung von Anweisungen für einen Überblick über die „Lambda-SQL“-Funktion, die nur für fortgeschrittene Anwendungsfälle bestimmt ist.

Parameter:
  • entity_or_base – eine zugeordnete Klasse oder eine Klasse, die eine Oberklasse einer bestimmten Gruppe von zugeordneten Klassen ist, auf die die Regel angewendet wird.

  • where_criteria

    ein Core SQL-Ausdruck, der einschränkende Kriterien anwendet. Dies kann auch ein „lambda:“ oder eine Python-Funktion sein, die eine Zielklasse als Argument akzeptiert, wenn die gegebene Klasse eine Basis mit vielen verschiedenen zugeordneten Unterklassen ist.

    Hinweis

    Um das Pickling zu unterstützen, verwenden Sie eine Modul-weite Python-Funktion, um den SQL-Ausdruck zu erzeugen, anstatt ein Lambda oder einen festen SQL-Ausdruck, die tendenziell nicht pickelbar sind.

  • include_aliases – wenn True, wird die Regel auch auf aliased()-Konstrukte angewendet.

  • propagate_to_loaders

    standardmäßig True, gilt für Beziehungs-Loader wie Lazy-Loader. Dies zeigt an, dass das Options-Objekt selbst einschließlich des SQL-Ausdrucks für jede geladene Instanz mitgeführt wird. Setzen Sie auf False, um zu verhindern, dass das Objekt einzelnen Instanzen zugewiesen wird.

    Siehe auch

    ORM-Abfrageereignisse - enthält Beispiele für die Verwendung von with_loader_criteria().

    Globale WHERE/ON-Kriterien hinzufügen - einfaches Beispiel, wie with_loader_criteria() mit dem Ereignis SessionEvents.do_orm_execute() kombiniert werden kann.

  • track_closure_variables

    wenn False, werden Closure-Variablen innerhalb eines Lambda-Ausdrucks nicht als Teil eines Cache-Schlüssels verwendet. Dies ermöglicht die Verwendung komplexerer Ausdrücke innerhalb eines Lambda-Ausdrucks, erfordert jedoch, dass das Lambda sicherstellt, dass es für eine gegebene Klasse bei jeder Ausführung denselben SQL-Ausdruck zurückgibt.

    Neu ab Version 1.4.0b2.

funktion sqlalchemy.orm.join(left: _FromClauseArgument, right: _FromClauseArgument, onclause: _OnClauseArgument | None = None, isouter: bool = False, full: bool = False) _ORMJoin

Erzeugt einen Inner Join zwischen linken und rechten Klauseln.

join() ist eine Erweiterung der Kern-Join-Schnittstelle, die von join() bereitgestellt wird, wobei die linke und rechte wählbare Elemente nicht nur Kern-wählbare Objekte wie Table sein können, sondern auch zugeordnete Klassen oder Instanzen von AliasedClass. Die „on“-Klausel kann ein SQL-Ausdruck oder ein ORM-zugeordnetes Attribut sein, das auf eine konfigurierte relationship() verweist.

join() wird im modernen Sprachgebrauch nicht häufig benötigt, da seine Funktionalität in der von den Methoden Select.join() und Query.join() gekapselt ist, die eine signifikante Automatisierung über join() hinaus selbst bieten. Die explizite Verwendung von join() mit ORM-aktivierten SELECT-Anweisungen beinhaltet die Verwendung der Methode Select.select_from(), wie in

from sqlalchemy.orm import join

stmt = (
    select(User)
    .select_from(join(User, Address, User.addresses))
    .filter(Address.email_address == "foo@bar.com")
)

Im modernen SQLAlchemy kann der obige Join prägnanter geschrieben werden als

stmt = (
    select(User)
    .join(User.addresses)
    .filter(Address.email_address == "foo@bar.com")
)

Warnung

Die direkte Verwendung von join() funktioniert möglicherweise nicht ordnungsgemäß mit modernen ORM-Optionen wie with_loader_criteria(). Es wird dringend empfohlen, die idiomatischen Join-Muster zu verwenden, die von Methoden wie Select.join() und Select.join_from() bereitgestellt werden, wenn ORM-Joins erstellt werden.

Siehe auch

Joins - im ORM Querying Guide für Hintergrundinformationen zu idiomatischen ORM-Join-Mustern

funktion sqlalchemy.orm.outerjoin(left: _FromClauseArgument, right: _FromClauseArgument, onclause: _OnClauseArgument | None = None, full: bool = False) _ORMJoin

Erzeugt einen Left Outer Join zwischen linken und rechten Klauseln.

Dies ist die „outer join“-Version der Funktion join() und weist das gleiche Verhalten auf, erzeugt aber einen OUTER JOIN. Weitere Nutzungsdetails finden Sie in der Dokumentation dieser Funktion.

funktion sqlalchemy.orm.with_parent(instance: object, prop: attributes.QueryableAttribute[Any], from_entity: _EntityType[Any] | None = None) ColumnElement[bool]

Erstellt ein Filterkriterium, das die primäre Entität dieser Abfrage mit der gegebenen verwandten Instanz über die konfigurierte relationship()-Konfiguration verbindet.

Z. B.

stmt = select(Address).where(with_parent(some_user, User.addresses))

Das gerenderte SQL ist dasselbe wie das, das gerendert wird, wenn ein Lazy-Loader von der gegebenen übergeordneten Entität für dieses Attribut ausgelöst würde, d.h. der entsprechende Zustand wird vom übergeordneten Objekt in Python übernommen, ohne dass Joins zur übergeordneten Tabelle in der gerenderten Anweisung gerendert werden müssen.

Die gegebene Eigenschaft kann auch PropComparator.of_type() verwenden, um die linke Seite der Kriterien anzugeben.

a1 = aliased(Address)
a2 = aliased(Address)
stmt = select(a1, a2).where(with_parent(u1, User.addresses.of_type(a2)))

Die obige Verwendung ist äquivalent zur Verwendung des Arguments from_entity().

a1 = aliased(Address)
a2 = aliased(Address)
stmt = select(a1, a2).where(
    with_parent(u1, User.addresses, from_entity=a2)
)
Parameter:
  • instance – Eine Instanz, die eine relationship() aufweist.

  • property – Klassenbezogenes Attribut, das angibt, welche Beziehung von der Instanz verwendet werden soll, um die Eltern-Kind-Beziehung abzugleichen.

  • from_entity

    Entität, die als linke Seite betrachtet werden soll. Standardmäßig ist dies die „Null“-Entität der Query selbst.

    Neu seit Version 1.2.