Automap

Definiert eine Erweiterung für das sqlalchemy.ext.declarative-System, das automatisch zugeordnete Klassen und Beziehungen aus einem Datenbankschema generiert, typischerweise, aber nicht notwendigerweise, aus einem, das reflektiert wurde.

Es wird gehofft, dass das AutomapBase-System eine schnelle und modernisierte Lösung für das Problem darstellt, das auch das sehr berühmte SQLSoup zu lösen versucht: die schnelle und rudimentäre Generierung eines Objektmodells aus einer bestehenden Datenbank im laufenden Betrieb. Indem das Problem strikt auf der Mapper-Konfigurationsebene angegangen und vollständig in bestehende Declarative-Klassentechniken integriert wird, versucht AutomapBase, einen gut integrierten Ansatz für die Problematik der schnellen automatischen Generierung von Ad-hoc-Abbildungen bereitzustellen.

Tipp

Die Automap-Erweiterung ist auf einen "Zero-Declaration"-Ansatz ausgerichtet, bei dem ein vollständiges ORM-Modell einschließlich Klassen und vordefinierter Beziehungen aus einem Datenbankschema im laufenden Betrieb generiert werden kann. Für Anwendungen, die weiterhin explizite Klassendefinitionen einschließlich expliziter Beziehungsdefinitionen in Verbindung mit der Reflexion von Tabellen verwenden möchten, ist die Klasse DeferredReflection, die unter Verwendung von DeferredReflection beschrieben wird, die bessere Wahl.

Grundlegende Verwendung

Die einfachste Verwendung besteht darin, eine bestehende Datenbank in ein neues Modell zu spiegeln. Wir erstellen eine neue AutomapBase-Klasse auf ähnliche Weise, wie wir eine deklarative Basisklasse erstellen, mit automap_base(). Anschließend rufen wir AutomapBase.prepare() auf der resultierenden Basisklasse auf und bitten sie, das Schema zu spiegeln und Abbildungen zu erstellen.

from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine

Base = automap_base()

# engine, suppose it has two tables 'user' and 'address' set up
engine = create_engine("sqlite:///mydatabase.db")

# reflect the tables
Base.prepare(autoload_with=engine)

# mapped classes are now created with names by default
# matching that of the table name.
User = Base.classes.user
Address = Base.classes.address

session = Session(engine)

# rudimentary relationships are produced
session.add(Address(email_address="foo@bar.com", user=User(name="foo")))
session.commit()

# collection-based relationships are by default named
# "<classname>_collection"
u1 = session.query(User).first()
print(u1.address_collection)

Das Aufrufen von AutomapBase.prepare() im obigen Beispiel mit dem Parameter AutomapBase.prepare.reflect gibt an, dass die Methode MetaData.reflect() für die MetaData-Sammlung dieser deklarativen Basisklassen aufgerufen wird; dann erhält jede gültige Table innerhalb der MetaData automatisch eine neue zugeordnete Klasse. Die ForeignKeyConstraint-Objekte, die die verschiedenen Tabellen miteinander verbinden, werden verwendet, um neue, bidirektionale relationship()-Objekte zwischen den Klassen zu erzeugen. Die Klassen und Beziehungen folgen einem Standardbenennungsschema, das wir anpassen können. An diesem Punkt ist unsere grundlegende Zuordnung, die aus den verwandten Klassen User und Address besteht, bereit für die traditionelle Verwendung.

Hinweis

Mit gültig meinen wir, dass eine Tabelle, um zugeordnet zu werden, einen Primärschlüssel angeben muss. Zusätzlich, wenn die Tabelle als reine Zuordnungstabelle zwischen zwei anderen Tabellen erkannt wird, wird sie nicht direkt zugeordnet, sondern stattdessen als Many-to-Many-Tabelle zwischen den Zuordnungen der beiden referenzierenden Tabellen konfiguriert.

Generieren von Abbildungen aus vorhandenen MetaData

Wir können ein bereits deklariertes MetaData-Objekt an automap_base() übergeben. Dieses Objekt kann auf jede Weise konstruiert werden, einschließlich programmgesteuert, aus einer serialisierten Datei oder durch Reflexion mit MetaData.reflect(). Unten illustrieren wir eine Kombination aus Reflexion und expliziter Tabellendeklaration.

from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey
from sqlalchemy.ext.automap import automap_base

engine = create_engine("sqlite:///mydatabase.db")

# produce our own MetaData object
metadata = MetaData()

# we can reflect it ourselves from a database, using options
# such as 'only' to limit what tables we look at...
metadata.reflect(engine, only=["user", "address"])

# ... or just define our own Table objects with it (or combine both)
Table(
    "user_order",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("user_id", ForeignKey("user.id")),
)

# we can then produce a set of mappings from this MetaData.
Base = automap_base(metadata=metadata)

# calling prepare() just sets up mapped classes and relationships.
Base.prepare()

# mapped classes are ready
User = Base.classes.user
Address = Base.classes.address
Order = Base.classes.user_order

Generieren von Abbildungen aus mehreren Schemas

Die Methode AutomapBase.prepare() kann beim Spiegeln Tabellen aus höchstens einem Schema gleichzeitig spiegeln, wobei der Parameter AutomapBase.prepare.schema verwendet wird, um den Namen des zu spiegelnden Schemas anzugeben. Um AutomapBase mit Tabellen aus mehreren Schemas zu füllen, kann AutomapBase.prepare() mehrmals aufgerufen werden, wobei jedes Mal ein anderer Name für den Parameter AutomapBase.prepare.schema übergeben wird. Die Methode AutomapBase.prepare() verwaltet eine interne Liste von Table-Objekten, die bereits zugeordnet wurden, und fügt nur für solche Table-Objekte neue Zuordnungen hinzu, die seit dem letzten Aufruf von AutomapBase.prepare() neu sind.

e = create_engine("postgresql://scott:tiger@localhost/test")

Base.metadata.create_all(e)

Base = automap_base()

Base.prepare(e)
Base.prepare(e, schema="test_schema")
Base.prepare(e, schema="test_schema_2")

Neu in Version 2.0: Die Methode AutomapBase.prepare() kann beliebig oft aufgerufen werden; nur neu hinzugefügte Tabellen werden bei jedem Durchlauf zugeordnet. Zuvor in Version 1.4 und früheren Versionen würden mehrfache Aufrufe zu Fehlern führen, da versucht würde, eine bereits zugeordnete Klasse erneut zuzuordnen. Der frühere Workaround-Ansatz, MetaData.reflect() direkt aufzurufen, bleibt ebenfalls verfügbar.

Abbilden von Tabellen mit demselben Namen über mehrere Schemas hinweg

Für den gängigen Fall, dass mehrere Schemas Tabellen mit demselben Namen haben und daher Klassen mit demselben Namen generieren würden, können Konflikte entweder durch die Verwendung des Hooks AutomapBase.prepare.classname_for_table zur Anwendung unterschiedlicher Klassennamen pro Schema oder durch die Verwendung des Hooks AutomapBase.prepare.modulename_for_table gelöst werden, der eine Disambiguierung von Klassen mit demselben Namen durch Änderung ihres effektiven __module__-Attributs ermöglicht. Im folgenden Beispiel wird dieser Hook verwendet, um ein __module__-Attribut für alle Klassen zu erstellen, das die Form mymodule.<schemaname> hat, wobei der Schema-Name default verwendet wird, wenn kein Schema vorhanden ist.

e = create_engine("postgresql://scott:tiger@localhost/test")

Base.metadata.create_all(e)


def module_name_for_table(cls, tablename, table):
    if table.schema is not None:
        return f"mymodule.{table.schema}"
    else:
        return f"mymodule.default"


Base = automap_base()

Base.prepare(e, modulename_for_table=module_name_for_table)
Base.prepare(
    e, schema="test_schema", modulename_for_table=module_name_for_table
)
Base.prepare(
    e, schema="test_schema_2", modulename_for_table=module_name_for_table
)

Die gleichnamigen Klassen sind in einer hierarchischen Sammlung organisiert, die unter AutomapBase.by_module verfügbar ist. Diese Sammlung wird mit dem punktgetrennten Namen eines bestimmten Pakets/Moduls bis hin zum gewünschten Klassennamen durchlaufen.

Hinweis

Bei Verwendung des Hooks AutomapBase.prepare.modulename_for_table, um einen neuen __module__ zurückzugeben, der nicht None ist, wird die Klasse nicht in die Sammlung AutomapBase.classes aufgenommen; nur Klassen, denen kein expliziter Modulname zugewiesen wurde, werden hier aufgenommen, da die Sammlung gleichnamige Klassen nicht einzeln darstellen kann.

Im obigen Beispiel, wenn die Datenbank eine Tabelle namens accounts in allen drei Schemas (standard, test_schema und test_schema_2) enthielt, werden drei separate Klassen verfügbar sein als:

Base.by_module.mymodule.default.accounts
Base.by_module.mymodule.test_schema.accounts
Base.by_module.mymodule.test_schema_2.accounts

Der Standardmodul-Namensraum, der für alle AutomapBase-Klassen generiert wird, ist sqlalchemy.ext.automap. Wenn kein Hook AutomapBase.prepare.modulename_for_table verwendet wird, sind die Inhalte von AutomapBase.by_module vollständig im Namensraum sqlalchemy.ext.automap enthalten (z. B. MyBase.by_module.sqlalchemy.ext.automap.<classname>), was die gleiche Reihe von Klassen enthält wie die, die in AutomapBase.classes zu sehen wären. Daher ist es im Allgemeinen nur notwendig, AutomapBase.by_module zu verwenden, wenn explizite __module__-Konventionen vorhanden sind.

Explizites Festlegen von Klassen

Tipp

Wenn explizite Klassen in einer Anwendung eine wichtige Rolle spielen sollen, sollten Sie stattdessen DeferredReflection in Betracht ziehen.

Die Erweiterung automap ermöglicht es, Klassen explizit zu definieren, ähnlich wie bei der Klasse DeferredReflection. Klassen, die von AutomapBase erben, verhalten sich wie normale deklarative Klassen, werden aber nicht sofort nach ihrer Erstellung zugeordnet, sondern erst, wenn wir AutomapBase.prepare() aufrufen. Die Methode AutomapBase.prepare() verwendet die von uns eingerichteten Klassen basierend auf dem verwendeten Tabellennamen. Wenn unser Schema die Tabellen user und address enthält, können wir eine oder beide Klassen definieren, die verwendet werden sollen.

from sqlalchemy.ext.automap import automap_base
from sqlalchemy import create_engine

# automap base
Base = automap_base()


# pre-declare User for the 'user' table
class User(Base):
    __tablename__ = "user"

    # override schema elements like Columns
    user_name = Column("name", String)

    # override relationships too, if desired.
    # we must use the same name that automap would use for the
    # relationship, and also must refer to the class name that automap will
    # generate for "address"
    address_collection = relationship("address", collection_class=set)


# reflect
engine = create_engine("sqlite:///mydatabase.db")
Base.prepare(autoload_with=engine)

# we still have Address generated from the tablename "address",
# but User is the same as Base.classes.User now

Address = Base.classes.address

u1 = session.query(User).first()
print(u1.address_collection)

# the backref is still there:
a1 = session.query(Address).first()
print(a1.user)

Eines der komplizierteren Details in dem obigen Beispiel ist, dass wir eines der relationship()-Objekte überschrieben haben, die Automap erstellt hätte. Um dies zu tun, mussten wir sicherstellen, dass die Namen mit dem übereinstimmen, was Automap normalerweise generiert, nämlich dass der Beziehungsname User.address_collection ist und der Name der Klasse, auf die verwiesen wird, aus Sicht von Automap address heißt, auch wenn wir uns mit Address auf diese beziehen, wenn wir diese Klasse verwenden.

Überschreiben von Benennungsschemata

Der automap-Erweiterung ist die Aufgabe zugewiesen, zugeordnete Klassen und Beziehungsnamen basierend auf einem Schema zu erstellen, was bedeutet, dass sie Entscheidungspunkte hat, wie diese Namen bestimmt werden. Diese drei Entscheidungspunkte werden über Funktionen bereitgestellt, die an die Methode AutomapBase.prepare() übergeben werden können und als classname_for_table(), name_for_scalar_relationship() und name_for_collection_relationship() bekannt sind. Diese Funktionen können ganz oder teilweise wie im folgenden Beispiel bereitgestellt werden, in dem wir ein "Camel Case"-Schema für Klassennamen und einen "Pluralizer" für Sammlungsnamen unter Verwendung des Pakets Inflect verwenden.

import re
import inflect


def camelize_classname(base, tablename, table):
    "Produce a 'camelized' class name, e.g."
    "'words_and_underscores' -> 'WordsAndUnderscores'"

    return str(
        tablename[0].upper()
        + re.sub(
            r"_([a-z])",
            lambda m: m.group(1).upper(),
            tablename[1:],
        )
    )


_pluralizer = inflect.engine()


def pluralize_collection(base, local_cls, referred_cls, constraint):
    "Produce an 'uncamelized', 'pluralized' class name, e.g."
    "'SomeTerm' -> 'some_terms'"

    referred_name = referred_cls.__name__
    uncamelized = re.sub(
        r"[A-Z]",
        lambda m: "_%s" % m.group(0).lower(),
        referred_name,
    )[1:]
    pluralized = _pluralizer.plural(uncamelized)
    return pluralized


from sqlalchemy.ext.automap import automap_base

Base = automap_base()

engine = create_engine("sqlite:///mydatabase.db")

Base.prepare(
    autoload_with=engine,
    classname_for_table=camelize_classname,
    name_for_collection_relationship=pluralize_collection,
)

Aus der obigen Zuordnung hätten wir nun die Klassen User und Address, wobei die Sammlung von User zu Address User.addresses heißt.

User, Address = Base.classes.User, Base.classes.Address

u1 = User(addresses=[Address(email="foo@bar.com")])

Beziehungserkennung

Der Großteil dessen, was Automap leistet, ist die Generierung von relationship()-Strukturen basierend auf Fremdschlüsseln. Der Mechanismus, wie dies für Many-to-One- und One-to-Many-Beziehungen funktioniert, ist wie folgt:

  1. Eine gegebene Table, von der bekannt ist, dass sie einer bestimmten Klasse zugeordnet ist, wird auf ForeignKeyConstraint-Objekte untersucht.

  2. Von jeder ForeignKeyConstraint wird die entfernte Table-Objekt, falls vorhanden, mit der Klasse abgeglichen, der sie zugeordnet werden soll, andernfalls wird sie übersprungen.

  3. Da die untersuchte ForeignKeyConstraint einer Referenz von der unmittelbar zugeordneten Klasse entspricht, wird die Beziehung als Many-to-One eingerichtet, die auf die referenzierte Klasse verweist; ein entsprechender One-to-Many-Backref wird auf der referenzierten Klasse erstellt, die auf diese Klasse verweist.

  4. Wenn eine der Spalten, die Teil der ForeignKeyConstraint ist, nicht nullable ist (z. B. nullable=False), wird ein Schlüsselwortargument relationship.cascade von all, delete-orphan zu den Schlüsselwortargumenten hinzugefügt, die an die Beziehung oder den Backref übergeben werden. Wenn die ForeignKeyConstraint angibt, dass ForeignKeyConstraint.ondelete für eine nicht-nullable oder SET NULL für eine nullable Spaltensammlung auf CASCADE gesetzt ist, wird das Flag relationship.passive_deletes in der Menge der Beziehungs-Schlüsselwortargumente auf True gesetzt. Beachten Sie, dass nicht alle Backends die Reflexion von ON DELETE unterstützen.

  5. Die Namen der Beziehungen werden mithilfe der aufrufbaren Funktionen AutomapBase.prepare.name_for_scalar_relationship und AutomapBase.prepare.name_for_collection_relationship ermittelt. Es ist wichtig zu beachten, dass die Standard-Beziehungsbenennung den Namen vom tatsächlichen Klassennamen ableitet. Wenn Sie einer bestimmten Klasse einen expliziten Namen gegeben haben, indem Sie sie deklariert haben, oder ein alternatives Schema für Klassennamen angegeben haben, ist dies der Name, von dem der Beziehungsname abgeleitet wird.

  6. Die Klassen werden auf eine vorhandene zugeordnete Eigenschaft mit diesen Namen untersucht. Wenn eine auf einer Seite vorhanden ist, aber auf der anderen Seite keine, versucht AutomapBase, auf der fehlenden Seite eine Beziehung zu erstellen, und verwendet dann den Parameter relationship.back_populates, um die neue Beziehung auf die andere Seite zu verweisen.

  7. Im üblichen Fall, in dem auf keiner Seite eine Beziehung besteht, erstellt AutomapBase.prepare() eine relationship() auf der "Many-to-One"-Seite und gleicht diese mit der anderen Seite über den Parameter relationship.backref ab.

  8. Die Erstellung der relationship() und optional der backref() wird an die Funktion AutomapBase.prepare.generate_relationship übergeben, die vom Endbenutzer bereitgestellt werden kann, um die an relationship() oder backref() übergebenen Argumente zu erweitern oder benutzerdefinierte Implementierungen dieser Funktionen zu nutzen.

Benutzerdefinierte Beziehungsargumente

Der Hook AutomapBase.prepare.generate_relationship kann verwendet werden, um Beziehungen Parameter hinzuzufügen. Für die meisten Fälle können wir die bestehende Funktion generate_relationship() verwenden, um das Objekt zurückzugeben, nachdem das gegebene Schlüsselwortwörterbuch mit unseren eigenen Argumenten erweitert wurde.

Unten ist eine Illustration, wie relationship.cascade und relationship.passive_deletes Optionen an alle One-to-Many-Beziehungen gesendet werden.

from sqlalchemy.ext.automap import generate_relationship
from sqlalchemy.orm import interfaces


def _gen_relationship(
    base, direction, return_fn, attrname, local_cls, referred_cls, **kw
):
    if direction is interfaces.ONETOMANY:
        kw["cascade"] = "all, delete-orphan"
        kw["passive_deletes"] = True
    # make use of the built-in function to actually return
    # the result.
    return generate_relationship(
        base, direction, return_fn, attrname, local_cls, referred_cls, **kw
    )


from sqlalchemy.ext.automap import automap_base
from sqlalchemy import create_engine

# automap base
Base = automap_base()

engine = create_engine("sqlite:///mydatabase.db")
Base.prepare(autoload_with=engine, generate_relationship=_gen_relationship)

Many-to-Many-Beziehungen

automap generiert Many-to-Many-Beziehungen, z. B. solche, die ein secondary-Argument enthalten. Der Prozess zur Erzeugung dieser ist wie folgt:

  1. Eine gegebene Table wird auf ForeignKeyConstraint-Objekte untersucht, bevor ihr eine zugeordnete Klasse zugewiesen wurde.

  2. Wenn die Tabelle zwei und exakt zwei ForeignKeyConstraint-Objekte enthält und alle Spalten innerhalb dieser Tabelle Mitglieder dieser beiden ForeignKeyConstraint-Objekte sind, wird die Tabelle als "sekundäre" Tabelle angenommen und nicht direkt zugeordnet.

  3. Die beiden (oder eine, für selbst-referenzielle) externen Tabellen, auf die die Table verweist, werden mit den Klassen abgeglichen, denen sie zugeordnet werden sollen, falls vorhanden.

  4. Wenn zugeordnete Klassen für beide Seiten gefunden werden, wird ein bidirektionales Many-to-Many-relationship() / backref()-Paar zwischen den beiden Klassen erstellt.

  5. Die Überschreibungslogik für Many-to-Many funktioniert genauso wie für One-to-Many/Many-to-One; die Funktion generate_relationship() wird aufgerufen, um die Strukturen zu generieren, und bestehende Attribute werden beibehalten.

Beziehungen mit Vererbung

automap generiert keine Beziehungen zwischen zwei Klassen, die sich in einer Vererbungsbeziehung befinden. Das heißt, mit zwei Klassen, die wie folgt gegeben sind:

class Employee(Base):
    __tablename__ = "employee"
    id = Column(Integer, primary_key=True)
    type = Column(String(50))
    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": type,
    }


class Engineer(Employee):
    __tablename__ = "engineer"
    id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }

Der Fremdschlüssel von Engineer zu Employee wird nicht für eine Beziehung verwendet, sondern zur Etablierung einer Joined-Vererbung zwischen den beiden Klassen.

Beachten Sie, dass dies bedeutet, dass Automap keine Beziehungen für Fremdschlüssel generiert, die von einer Unterklasse zu einer Oberklasse verknüpfen. Wenn eine Zuordnung tatsächliche Beziehungen von Unterklasse zu Oberklasse enthält, müssen diese explizit sein. Unten, da wir zwei separate Fremdschlüssel von Engineer zu Employee haben, müssen wir sowohl die gewünschte Beziehung als auch die inherit_condition einrichten, da dies Dinge sind, die SQLAlchemy nicht erraten kann.

class Employee(Base):
    __tablename__ = "employee"
    id = Column(Integer, primary_key=True)
    type = Column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": type,
    }


class Engineer(Employee):
    __tablename__ = "engineer"
    id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
    favorite_employee_id = Column(Integer, ForeignKey("employee.id"))

    favorite_employee = relationship(
        Employee, foreign_keys=favorite_employee_id
    )

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
        "inherit_condition": id == Employee.id,
    }

Behandlung einfacher Namenskonflikte

Bei Namenskonflikten während der Zuordnung überschreiben Sie bei Bedarf classname_for_table(), name_for_scalar_relationship() und name_for_collection_relationship(). Zum Beispiel, wenn Automap versucht, eine Many-to-One-Beziehung genauso zu benennen wie eine vorhandene Spalte, kann bedingt eine alternative Konvention ausgewählt werden. Angenommen, ein Schema:

CREATE TABLE table_a (
    id INTEGER PRIMARY KEY
);

CREATE TABLE table_b (
    id INTEGER PRIMARY KEY,
    table_a INTEGER,
    FOREIGN KEY(table_a) REFERENCES table_a(id)
);

Das obige Schema ordnet zunächst die table_a-Tabelle als Klasse namens table_a zu. Anschließend ordnet es eine Beziehung auf die Klasse für table_b mit demselben Namen wie diese zugeordnete Klasse, z. B. table_a, zu. Dieser Beziehungsname steht im Konflikt mit der Mapping-Spalte table_b.table_a und führt bei der Zuordnung zu einem Fehler.

Diesen Konflikt können wir wie folgt durch die Verwendung eines Unterstrichs lösen.

def name_for_scalar_relationship(
    base, local_cls, referred_cls, constraint
):
    name = referred_cls.__name__.lower()
    local_table = local_cls.__table__
    if name in local_table.columns:
        newname = name + "_"
        warnings.warn(
            "Already detected name %s present.  using %s" % (name, newname)
        )
        return newname
    return name


Base.prepare(
    autoload_with=engine,
    name_for_scalar_relationship=name_for_scalar_relationship,
)

Alternativ können wir den Namen auf der Spaltenseite ändern. Die zugeordneten Spalten können mit der unter Explicitly Naming Declarative Mapped Columns beschriebenen Technik modifiziert werden, indem die Spalte explizit einem neuen Namen zugewiesen wird.

Base = automap_base()


class TableB(Base):
    __tablename__ = "table_b"
    _table_a = Column("table_a", ForeignKey("table_a.id"))


Base.prepare(autoload_with=engine)

Automap mit expliziten Deklarationen verwenden

Wie bereits erwähnt, ist Automap unabhängig von der Reflexion und kann jede Sammlung von Table-Objekten innerhalb einer MetaData-Sammlung nutzen. Daraus folgt, dass Automap auch verwendet werden kann, um fehlende Beziehungen zu generieren, wenn ein ansonsten vollständiges Modell die Tabellenmetadaten vollständig definiert.

from sqlalchemy.ext.automap import automap_base
from sqlalchemy import Column, Integer, String, ForeignKey

Base = automap_base()


class User(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True)
    name = Column(String)


class Address(Base):
    __tablename__ = "address"

    id = Column(Integer, primary_key=True)
    email = Column(String)
    user_id = Column(ForeignKey("user.id"))


# produce relationships
Base.prepare()

# mapping is complete, with "address_collection" and
# "user" relationships
a1 = Address(email="u1")
a2 = Address(email="u2")
u1 = User(address_collection=[a1, a2])
assert a1.user is u1

Oben, bei weitgehend vollständigen User und Address-Mappings, ermöglichte der auf Address.user_id definierte ForeignKey die Generierung eines bidirektionalen Beziehungspaares Address.user und User.address_collection auf den zugeordneten Klassen.

Beachten Sie, dass bei der Unterklassifizierung von AutomapBase die Methode AutomapBase.prepare() erforderlich ist; wenn sie nicht aufgerufen wird, befinden sich die von uns deklarierten Klassen in einem nicht zugeordneten Zustand.

Spaltendefinitionen abfangen

Die Objekte MetaData und Table unterstützen einen Event-Hook DDLEvents.column_reflect(), der verwendet werden kann, um die von einer Datenbankspalte reflektierten Informationen abzufangen, bevor das Column-Objekt konstruiert wird. Wenn wir beispielsweise Spalten nach einer Benennungskonvention wie "attr_<columnname>" zuordnen wollten, könnte das Ereignis wie folgt angewendet werden:

@event.listens_for(Base.metadata, "column_reflect")
def column_reflect(inspector, table, column_info):
    # set column.key = "attr_<lower_case_name>"
    column_info["key"] = "attr_%s" % column_info["name"].lower()


# run reflection
Base.prepare(autoload_with=engine)

Neu in Version 1.4.0b2: Das Ereignis DDLEvents.column_reflect() kann auf ein MetaData-Objekt angewendet werden.

API-Referenz

Objektname Beschreibung

automap_base([declarative_base], **kw)

Erzeugt eine deklarative Automap-Basis.

AutomapBase

Basisklasse für ein „Automap“-Schema.

classname_for_table(base, tablename, table)

Gibt den Klassennamen zurück, der für einen Tabellennamen verwendet werden soll.

generate_relationship(base, direction, return_fn, attrname, ..., **kw)

Generiert ein relationship() oder backref() im Auftrag von zwei zugeordneten Klassen.

name_for_collection_relationship(base, local_cls, referred_cls, constraint)

Gibt den Attributnamen zurück, der verwendet werden soll, um von einer Klasse zu einer anderen für eine Sammlungsreferenz zu verweisen.

name_for_scalar_relationship(base, local_cls, referred_cls, constraint)

Gibt den Attributnamen zurück, der verwendet werden soll, um von einer Klasse zu einer anderen für eine Skalarobjekt-Referenz zu verweisen.

Funktion sqlalchemy.ext.automap.automap_base(declarative_base: Type[Any] | None = None, **kw: Any) Any

Erzeugt eine deklarative Automap-Basis.

Diese Funktion erzeugt eine neue Basisklasse, die ein Produkt der Klasse AutomapBase sowie einer deklarativen Basis ist, die von declarative_base() erzeugt wurde.

Alle Parameter außer declarative_base sind Schlüsselwortargumente, die direkt an die Funktion declarative_base() übergeben werden.

Parameter:
  • declarative_base – eine vorhandene Klasse, die von declarative_base() erzeugt wurde. Wenn dies übergeben wird, ruft die Funktion declarative_base() nicht mehr selbst auf und alle anderen Schlüsselwortargumente werden ignoriert.

  • **kw – Schlüsselwortargumente werden an declarative_base() weitergegeben.

Klasse sqlalchemy.ext.automap.AutomapBase

Basisklasse für ein „Automap“-Schema.

Die Klasse AutomapBase kann mit der „deklarativen Basisklasse“ verglichen werden, die von der Funktion declarative_base() erzeugt wird. In der Praxis wird die Klasse AutomapBase immer als Mixin zusammen mit einer tatsächlichen deklarativen Basis verwendet.

Eine neue, unterklassifizierbare AutomapBase wird typischerweise über die Funktion automap_base() instanziiert.

Siehe auch

Automap

Attribut sqlalchemy.ext.automap.AutomapBase.by_module: ClassVar[ByModuleProperties]

Eine Instanz von Properties, die eine hierarchische Struktur von punktgetrennten Modulnamen, die mit Klassen verknüpft sind, enthält.

Diese Sammlung ist eine Alternative zur Sammlung AutomapBase.classes und nützlich, wenn der Parameter AutomapBase.prepare.modulename_for_table verwendet wird, der verschiedenen __module__-Attributen für generierte Klassen zugewiesen wird.

Das Standard-__module__ einer von Automap generierten Klasse ist sqlalchemy.ext.automap; der Zugriff auf diesen Namensraum über AutomapBase.by_module sieht wie folgt aus:

User = Base.by_module.sqlalchemy.ext.automap.User

Wenn eine Klasse einen __module__ von mymodule.account hätte, sieht der Zugriff auf diesen Namensraum wie folgt aus:

MyClass = Base.by_module.mymodule.account.MyClass

Neu in Version 2.0.

Attribut sqlalchemy.ext.automap.AutomapBase.classes: ClassVar[Properties[Type[Any]]]

Eine Instanz von Properties, die Klassen enthält.

Dieses Objekt verhält sich weitgehend wie die Sammlung .c einer Tabelle. Klassen sind unter dem Namen vorhanden, unter dem sie gegeben wurden, z. B.

Base = automap_base()
Base.prepare(autoload_with=some_engine)

User, Address = Base.classes.User, Base.classes.Address

Für Klassennamen, die mit einem Methodennamen von Properties, wie z. B. items(), kollidieren, wird auch die Getitem-Form unterstützt.

Item = Base.classes["items"]
Attribut sqlalchemy.ext.automap.AutomapBase.metadata: ClassVar[MetaData]

Bezieht sich auf die MetaData-Sammlung, die für neue Table-Objekte verwendet wird.

Klassenmethode sqlalchemy.ext.automap.AutomapBase.prepare(autoload_with: Engine | None = None, engine: Any | None = None, reflect: bool = False, schema: str | None = None, classname_for_table: PythonNameForTableType | None = None, modulename_for_table: PythonNameForTableType | None = None, collection_class: Any | None = None, name_for_scalar_relationship: NameForScalarRelationshipType | None = None, name_for_collection_relationship: NameForCollectionRelationshipType | None = None, generate_relationship: GenerateRelationshipType | None = None, reflection_options: Dict[_KT, _VT] | immutabledict[_KT, _VT] = {}) None

Extrahiert zugeordnete Klassen und Beziehungen aus den MetaData und führt Zuordnungen durch.

Für eine vollständige Dokumentation und Beispiele siehe Grundlegende Verwendung.

Parameter:
  • autoload_with – eine Engine oder ein Connection, mit der/dem eine Schemareflexion durchgeführt werden soll; wenn angegeben, wird die Methode MetaData.reflect() im Geltungsbereich dieser Methode aufgerufen.

  • engine

    Legacy; verwenden Sie AutomapBase.autoload_with. Wird verwendet, um die Engine oder Connection anzugeben, mit der Tabellen reflektiert werden sollen, wenn AutomapBase.reflect True ist.

    Veraltet seit Version 1.4: Der Parameter AutomapBase.prepare.engine ist veraltet und wird in einer zukünftigen Version entfernt. Bitte verwenden Sie den Parameter AutomapBase.prepare.autoload_with.

  • reflect

    Legacy; verwenden Sie AutomapBase.autoload_with. Zeigt an, dass MetaData.reflect() aufgerufen werden soll.

    Veraltet seit Version 1.4: Der Parameter AutomapBase.prepare.reflect ist veraltet und wird in einer zukünftigen Version entfernt. Reflexion wird aktiviert, wenn AutomapBase.prepare.autoload_with übergeben wird.

  • classname_for_table – aufrufbare Funktion, die verwendet wird, um neue Klassennamen zu erzeugen, gegeben einen Tabellennamen. Standardmäßig classname_for_table().

  • modulename_for_table

    aufrufbare Funktion, die verwendet wird, um das effektive __module__ für eine intern generierte Klasse zu erzeugen, um mehrere Klassen mit demselben Namen in einer einzigen Automap-Basis zu ermöglichen, die sich in unterschiedlichen „Modulen“ befinden würden.

    Standardmäßig None, was angibt, dass __module__ nicht explizit gesetzt wird; die Python-Laufzeitumgebung verwendet den Wert sqlalchemy.ext.automap für diese Klassen.

    Bei der Zuweisung von __module__ zu generierten Klassen können diese anhand punktgetrennter Modulnamen über die Sammlung AutomapBase.by_module abgerufen werden. Klassen, denen ein explizites __module__ über diesen Hook zugewiesen wurde, werden **nicht** in die Sammlung AutomapBase.classes, sondern nur in AutomapBase.by_module aufgenommen.

    Neu in Version 2.0.

  • name_for_scalar_relationship – aufrufbare Funktion, die verwendet wird, um Beziehungsnamen für Skalar-Beziehungen zu erzeugen. Standardmäßig name_for_scalar_relationship().

  • name_for_collection_relationship – aufrufbare Funktion, die verwendet wird, um Beziehungsnamen für sammlungsorientierte Beziehungen zu erzeugen. Standardmäßig name_for_collection_relationship().

  • generate_relationship – aufrufbare Funktion, die verwendet wird, um tatsächlich relationship() und backref()-Konstrukte zu generieren. Standardmäßig generate_relationship().

  • collection_class – die Python-Sammlungsklasse, die verwendet wird, wenn ein neues relationship()-Objekt erstellt wird, das eine Sammlung darstellt. Standardmäßig list.

  • schema

    Schemaname, der bei der Reflexion von Tabellen unter Verwendung des Parameters AutomapBase.prepare.autoload_with reflektiert werden soll. Der Name wird an den Parameter MetaData.reflect.schema von MetaData.reflect() übergeben. Wenn dieser weggelassen wird, wird das Standard-Schema der Datenbankverbindung verwendet.

    Hinweis

    Der Parameter AutomapBase.prepare.schema unterstützt die Reflexion eines einzelnen Schemas gleichzeitig. Um Tabellen aus vielen Schemas einzubeziehen, verwenden Sie mehrere Aufrufe von AutomapBase.prepare().

    Eine Übersicht über die Generierung von Mappings aus mehreren Schemas, einschließlich der Verwendung zusätzlicher Benennungskonventionen zur Lösung von Tabellennamenkonflikten, finden Sie im Abschnitt Generierung von Mappings aus mehreren Schemas.

    Neu in Version 2.0: AutomapBase.prepare() kann beliebig oft direkt aufgerufen werden, wobei die bereits verarbeiteten Tabellen verfolgt werden, um eine erneute Verarbeitung zu vermeiden.

  • reflection_options

    Wenn vorhanden, wird dieses Dictionary von Optionen an MetaData.reflect() übergeben, um allgemeine reflektionsspezifische Optionen wie only und/oder dialektspezifische Optionen wie oracle_resolve_synonyms bereitzustellen.

    Neu in Version 1.4.

Funktion sqlalchemy.ext.automap.classname_for_table(base: Type[Any], tablename: str, table: Table) str

Gibt den Klassennamen zurück, der für einen Tabellennamen verwendet werden soll.

Die Standardimplementierung ist:

return str(tablename)

Alternative Implementierungen können über den Parameter AutomapBase.prepare.classname_for_table angegeben werden.

Parameter:
  • base – die AutomapBase-Klasse, die die Vorbereitung durchführt.

  • tablename – Zeichenkettenname der Table.

  • table – das Table-Objekt selbst.

Gibt zurück:

eine Zeichenkettenklasse.

Hinweis

In Python 2 muss der für den Klassennamen verwendete String ein Nicht-Unicode-Objekt sein, z. B. ein str()-Objekt. Das Attribut .name von Table ist typischerweise eine Python-Unicode-Unterklasse, daher sollte die Funktion str() nach Berücksichtigung von Nicht-ASCII-Zeichen auf diesen Namen angewendet werden.

function sqlalchemy.ext.automap.name_for_scalar_relationship(base: Type[Any], local_cls: Type[Any], referred_cls: Type[Any], constraint: ForeignKeyConstraint) str

Gibt den Attributnamen zurück, der verwendet werden soll, um von einer Klasse zu einer anderen für eine Skalarobjekt-Referenz zu verweisen.

Die Standardimplementierung ist:

return referred_cls.__name__.lower()

Alternative Implementierungen können über den Parameter AutomapBase.prepare.name_for_scalar_relationship angegeben werden.

Parameter:
  • base – die AutomapBase-Klasse, die die Vorbereitung durchführt.

  • local_cls – die Klasse, die auf der lokalen Seite abgebildet werden soll.

  • referred_cls – die Klasse, die auf der verweisenden Seite abgebildet werden soll.

  • constraint – die ForeignKeyConstraint, die zur Erzeugung dieser Beziehung inspiziert wird.

function sqlalchemy.ext.automap.name_for_collection_relationship(base: Type[Any], local_cls: Type[Any], referred_cls: Type[Any], constraint: ForeignKeyConstraint) str

Gibt den Attributnamen zurück, der verwendet werden soll, um von einer Klasse zu einer anderen für eine Sammlungsreferenz zu verweisen.

Die Standardimplementierung ist:

return referred_cls.__name__.lower() + "_collection"

Alternative Implementierungen können über den Parameter AutomapBase.prepare.name_for_collection_relationship angegeben werden.

Parameter:
  • base – die AutomapBase-Klasse, die die Vorbereitung durchführt.

  • local_cls – die Klasse, die auf der lokalen Seite abgebildet werden soll.

  • referred_cls – die Klasse, die auf der verweisenden Seite abgebildet werden soll.

  • constraint – die ForeignKeyConstraint, die zur Erzeugung dieser Beziehung inspiziert wird.

function sqlalchemy.ext.automap.generate_relationship(base: Type[Any], direction: RelationshipDirection, return_fn: Callable[..., Relationship[Any]] | Callable[..., ORMBackrefArgument], attrname: str, local_cls: Type[Any], referred_cls: Type[Any], **kw: Any) Relationship[Any] | ORMBackrefArgument

Generiert ein relationship() oder backref() im Auftrag von zwei zugeordneten Klassen.

Eine alternative Implementierung dieser Funktion kann über den Parameter AutomapBase.prepare.generate_relationship angegeben werden.

Die Standardimplementierung dieser Funktion lautet wie folgt:

if return_fn is backref:
    return return_fn(attrname, **kw)
elif return_fn is relationship:
    return return_fn(referred_cls, **kw)
else:
    raise TypeError("Unknown relationship function: %s" % return_fn)
Parameter:
  • base – die AutomapBase-Klasse, die die Vorbereitung durchführt.

  • direction – gibt die „Richtung“ der Beziehung an; dies ist eine von ONETOMANY, MANYTOONE, MANYTOMANY.

  • return_fn – die Funktion, die standardmäßig zum Erstellen der Beziehung verwendet wird. Dies ist entweder relationship() oder backref(). Das Ergebnis der Funktion backref() wird in einem zweiten Schritt verwendet, um eine neue relationship() zu erzeugen, daher ist es entscheidend, dass benutzerdefinierte Implementierungen korrekt zwischen den beiden Funktionen unterscheiden, wenn eine benutzerdefinierte Beziehungsfunktion verwendet wird.

  • attrname – der Attributname, dem diese Beziehung zugewiesen wird. Wenn der Wert von generate_relationship.return_fn die Funktion backref() ist, dann ist dieser Name der Name, der dem Backref zugewiesen wird.

  • local_cls – die „lokale“ Klasse, zu der diese Beziehung oder dieser Backref lokal gehören wird.

  • referred_cls – die „referenzierte“ Klasse, auf die sich die Beziehung oder der Backref bezieht.

  • **kw – alle zusätzlichen Schlüsselwortargumente werden an die Funktion weitergegeben.

Gibt zurück:

ein relationship()- oder backref()-Konstrukt, wie durch den Parameter generate_relationship.return_fn vorgegeben.