Deklarative Erweiterungen

Erweiterungen, die spezifisch für die deklarative Mapping-API sind.

Geändert in Version 1.4: Der Großteil der deklarativen Erweiterung ist nun in die SQLAlchemy ORM integriert und kann aus dem Namensraum sqlalchemy.orm importiert werden. Siehe die Dokumentation unter Deklaratives Mapping für neue Dokumentation. Für einen Überblick über die Änderung siehe Deklarativ ist nun in die ORM integriert mit neuen Funktionen.

Objektname Beschreibung

AbstractConcreteBase

Eine Hilfsklasse für „konkrete“ deklarative Mappings.

ConcreteBase

Eine Hilfsklasse für „konkrete“ deklarative Mappings.

DeferredReflection

Eine Hilfsklasse zur Erstellung von Mappings basierend auf einem verzögerten Reflexionsschritt.

class sqlalchemy.ext.declarative.AbstractConcreteBase

Eine Hilfsklasse für „konkrete“ deklarative Mappings.

AbstractConcreteBase verwendet automatisch die Funktion polymorphic_union() für alle Tabellen, die als Unterklassen dieser Klasse gemappt sind. Die Funktion wird über die Funktion __declare_first__() aufgerufen, die im Wesentlichen ein Hook für das Ereignis before_configured() ist.

AbstractConcreteBase wendet Mapper für seine unmittelbar vererbende Klasse an, wie es für jede andere deklarativ gemappte Klasse geschehen würde. Der Mapper ist jedoch nicht auf ein bestimmtes Table-Objekt abgebildet. Stattdessen wird er direkt auf das von polymorphic_union() erzeugte „polymorphe“ Selektionsergebnis abgebildet und führt keine eigenen Persistenzoperationen durch. Im Vergleich zu ConcreteBase, das seine unmittelbar vererbende Klasse auf eine tatsächliche Table abbildet, die Zeilen direkt speichert.

Hinweis

Die Klasse AbstractConcreteBase verzögert die Mapper-Erstellung der Basisklasse, bis alle Unterklassen definiert sind, da sie eine Abbildung auf ein Selektionsergebnis erstellen muss, das alle Unterklassentabellen umfasst. Um dies zu erreichen, wartet sie auf das Mapper-Konfigurationsereignis, woraufhin sie alle konfigurierten Unterklassen durchscannt und eine Abbildung einrichtet, die alle Unterklassen gleichzeitig abfragt.

Obwohl dieses Ereignis normalerweise automatisch ausgelöst wird, kann es im Fall von AbstractConcreteBase notwendig sein, es explizit aufzurufen, nachdem alle Unterklassen-Mappings definiert wurden, falls die erste Operation eine Abfrage auf diese Basisklasse sein soll. Um dies zu tun, nachdem alle gewünschten Klassen konfiguriert wurden, kann die Methode registry.configure() auf der verwendeten registry aufgerufen werden, die in Bezug auf eine bestimmte deklarative Basisklasse verfügbar ist.

Base.registry.configure()

Beispiel

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.ext.declarative import AbstractConcreteBase


class Base(DeclarativeBase):
    pass


class Employee(AbstractConcreteBase, Base):
    pass


class Manager(Employee):
    __tablename__ = "manager"
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }


Base.registry.configure()

Die abstrakte Basisklasse wird von der Deklarativität auf besondere Weise behandelt; zum Zeitpunkt der Klassenkonfiguration verhält sie sich wie ein deklaratives Mixin oder eine __abstract__-Basisklasse. Sobald Klassen konfiguriert und Mappings erstellt sind, wird sie selbst abgebildet, aber nach allen ihren Nachkommen. Dies ist ein sehr einzigartiges Abbildungssystem, das in keiner anderen SQLAlchemy-API-Funktion zu finden ist.

Mit diesem Ansatz können wir Spalten und Eigenschaften angeben, die in den gemappten Unterklassen Platz finden, so wie wir es normalerweise in Mixin- und benutzerdefinierte Basisklassen tun.

from sqlalchemy.ext.declarative import AbstractConcreteBase


class Company(Base):
    __tablename__ = "company"
    id = Column(Integer, primary_key=True)


class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    employee_id = Column(Integer, primary_key=True)

    @declared_attr
    def company_id(cls):
        return Column(ForeignKey("company.id"))

    @declared_attr
    def company(cls):
        return relationship("Company")


class Manager(Employee):
    __tablename__ = "manager"

    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }


Base.registry.configure()

Wenn wir jedoch unsere Mappings verwenden, werden sowohl Manager als auch Employee ein unabhängig nutzbares Attribut .company haben.

session.execute(select(Employee).filter(Employee.company.has(id=5)))
Parameter:

strict_attrs

Wenn dies auf der Basisklasse angegeben ist, wird der Modus „strikte“ Attribute aktiviert, der versucht, ORM-gemappte Attribute auf der Basisklasse auf diejenigen zu beschränken, die sofort vorhanden sind, während das „polymorphe“ Ladeverhalten beibehalten wird.

Neu in Version 2.0.

class sqlalchemy.ext.declarative.ConcreteBase

Eine Hilfsklasse für „konkrete“ deklarative Mappings.

ConcreteBase verwendet automatisch die Funktion polymorphic_union() für alle Tabellen, die als Unterklassen dieser Klasse gemappt sind. Die Funktion wird über die Funktion __declare_last__() aufgerufen, die im Wesentlichen ein Hook für das Ereignis after_configured() ist.

ConcreteBase erzeugt eine abgebildete Tabelle für die Klasse selbst. Im Vergleich zu AbstractConcreteBase, die dies nicht tut.

Beispiel

from sqlalchemy.ext.declarative import ConcreteBase


class Employee(ConcreteBase, Base):
    __tablename__ = "employee"
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "concrete": True,
    }


class Manager(Employee):
    __tablename__ = "manager"
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))
    __mapper_args__ = {
        "polymorphic_identity": "manager",
        "concrete": True,
    }

Der Name der von polymorphic_union() verwendeten Diskriminatorspalte ist standardmäßig type. Um dem Anwendungsfall eines Mappings gerecht zu werden, bei dem eine tatsächliche Spalte in einer abgebildeten Tabelle bereits type heißt, kann der Diskriminatorname durch Setzen des Attributs _concrete_discriminator_name konfiguriert werden.

class Employee(ConcreteBase, Base):
    _concrete_discriminator_name = "_concrete_discriminator"

Neu in Version 1.3.19: Das Attribut _concrete_discriminator_name wurde zu ConcreteBase hinzugefügt, damit der Name der virtuellen Diskriminatorspalte angepasst werden kann.

Geändert in Version 1.4.2: Das Attribut _concrete_discriminator_name muss nur auf der untersten Klasse platziert werden, um für alle Unterklassen korrekt zu wirken. Es wird nun eine explizite Fehlermeldung ausgegeben, wenn die Spaltennamen mit dem Diskriminatornamen kollidieren, während in der 1.3.x-Serie einige Warnungen ausgegeben und dann eine nicht nützliche Abfrage generiert wurde.

class sqlalchemy.ext.declarative.DeferredReflection

Eine Hilfsklasse zur Erstellung von Mappings basierend auf einem verzögerten Reflexionsschritt.

Normalerweise kann die Deklarativität mit Reflexion verwendet werden, indem ein Table-Objekt mit autoload_with=engine als Attribut __table__ auf einer deklarativen Klasse gesetzt wird. Der Nachteil ist, dass die Table vollständig reflektiert sein muss oder zumindest eine Primärschlüsselspalte haben muss, wenn eine normale deklarative Abbildung erstellt wird, was bedeutet, dass die Engine zum Zeitpunkt der Klassendeklaration verfügbar sein muss.

Das Mixin DeferredReflection verschiebt die Erstellung von Mappern auf einen späteren Zeitpunkt, nachdem eine bestimmte Methode aufgerufen wurde, die zuerst alle bisher erstellten Table-Objekte reflektiert. Klassen können es wie folgt definieren:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import DeferredReflection

Base = declarative_base()


class MyClass(DeferredReflection, Base):
    __tablename__ = "mytable"

Oben ist MyClass noch nicht abgebildet. Nachdem eine Reihe von Klassen wie oben definiert wurden, können alle Tabellen reflektiert und Mappings mit prepare() erstellt werden.

engine = create_engine("someengine://...")
DeferredReflection.prepare(engine)

Das Mixin DeferredReflection kann auf einzelne Klassen angewendet, als Basis für die deklarative Basis selbst verwendet oder in einer benutzerdefinierten abstrakten Klasse verwendet werden. Die Verwendung einer abstrakten Basis ermöglicht es, dass nur eine Teilmenge von Klassen für einen bestimmten Vorbereitungsschritt vorbereitet wird, was für Anwendungen notwendig ist, die mehr als eine Engine verwenden. Wenn eine Anwendung beispielsweise zwei Engines hat, können Sie zwei Basen verwenden und jede separat vorbereiten, z. B.

class ReflectedOne(DeferredReflection, Base):
    __abstract__ = True


class ReflectedTwo(DeferredReflection, Base):
    __abstract__ = True


class MyClass(ReflectedOne):
    __tablename__ = "mytable"


class MyOtherClass(ReflectedOne):
    __tablename__ = "myothertable"


class YetAnotherClass(ReflectedTwo):
    __tablename__ = "yetanothertable"


# ... etc.

Oben können die Klassenhierarchien für ReflectedOne und ReflectedTwo separat konfiguriert werden.

ReflectedOne.prepare(engine_one)
ReflectedTwo.prepare(engine_two)

Mitglieder

prepare()

classmethod sqlalchemy.ext.declarative.DeferredReflection.prepare(bind: Engine | Connection, **reflect_kw: Any) None

Reflektiert alle Table-Objekte für alle aktuellen DeferredReflection-Unterklassen

Parameter: