SQLAlchemy 2.0 Dokumentation
- Vorher: SQLAlchemy ORM
- Nächste: ORM Gemappte Klasseneinstellungen
- Nach oben: Startseite
- Auf dieser Seite
ORM Schnellstart¶
Für neue Benutzer, die schnell sehen möchten, wie grundlegende ORM-Nutzung aussieht, hier ist eine abgekürzte Form der Abbildungen und Beispiele aus dem SQLAlchemy Unified Tutorial. Der Code hier ist von einer sauberen Befehlszeile aus vollständig ausführbar.
Da die Beschreibungen in diesem Abschnitt absichtlich sehr kurz sind, fahren Sie bitte mit dem vollständigen SQLAlchemy Unified Tutorial für eine viel detailliertere Beschreibung jedes der hier dargestellten Konzepte fort.
Geändert in Version 2.0: Der ORM Schnellstart wurde für die neuesten PEP 484-konformen Funktionen unter Verwendung neuer Konstrukte wie mapped_column() aktualisiert. Informationen zur Migration finden Sie im Abschnitt ORM Deklarative Modelle.
Modelle deklarieren¶
Hier definieren wir Konstrukte auf Modulebene, die die Strukturen bilden, aus denen wir aus der Datenbank abfragen werden. Diese Struktur, bekannt als Deklarative Abbildung, definiert gleichzeitig ein Python-Objektmodell sowie Datenbankmetadaten, die tatsächliche SQL-Tabellen beschreiben, die in einer bestimmten Datenbank existieren oder existieren werden.
>>> from typing import List
>>> from typing import Optional
>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy import String
>>> from sqlalchemy.orm import DeclarativeBase
>>> from sqlalchemy.orm import Mapped
>>> from sqlalchemy.orm import mapped_column
>>> from sqlalchemy.orm import relationship
>>> class Base(DeclarativeBase):
... pass
>>> class User(Base):
... __tablename__ = "user_account"
...
... id: Mapped[int] = mapped_column(primary_key=True)
... name: Mapped[str] = mapped_column(String(30))
... fullname: Mapped[Optional[str]]
...
... addresses: Mapped[List["Address"]] = relationship(
... back_populates="user", cascade="all, delete-orphan"
... )
...
... def __repr__(self) -> str:
... return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
>>> class Address(Base):
... __tablename__ = "address"
...
... id: Mapped[int] = mapped_column(primary_key=True)
... email_address: Mapped[str]
... user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
...
... user: Mapped["User"] = relationship(back_populates="addresses")
...
... def __repr__(self) -> str:
... return f"Address(id={self.id!r}, email_address={self.email_address!r})"Die Abbildung beginnt mit einer Basisklasse, die oben Base genannt wird und durch eine einfache Unterklasse der Klasse DeclarativeBase erstellt wird.
Individuelle abgebildete Klassen werden dann durch Erstellen von Unterklassen von Base erstellt. Eine abgebildete Klasse bezieht sich typischerweise auf eine einzige bestimmte Datenbanktabelle, deren Name durch das Klassenattribut __tablename__ angegeben wird.
Als nächstes werden Spalten, die Teil der Tabelle sind, deklariert, indem Attribute hinzugefügt werden, die eine spezielle Typannotation namens Mapped enthalten. Der Name jedes Attributs entspricht der Spalte, die Teil der Datenbanktabelle sein soll. Der Datentyp jeder Spalte wird zuerst aus dem Python-Datentyp übernommen, der mit jeder Mapped-Annotation verbunden ist; int für INTEGER, str für VARCHAR usw. Die Nullbarkeit ergibt sich daraus, ob der Typmodifikator Optional[] verwendet wird. Spezifischere Typinformationen können unter Verwendung von SQLAlchemy-Typobjekten auf der rechten Seite der mapped_column()-Direktive angegeben werden, wie z. B. der String-Datentyp, der oben in der Spalte User.name verwendet wurde. Die Zuordnung zwischen Python-Typen und SQL-Typen kann über die Typen-Annotation-Map angepasst werden.
Die mapped_column()-Direktive wird für alle spaltenbasierten Attribute verwendet, die eine spezifischere Anpassung erfordern. Neben Typinformationen akzeptiert diese Direktive eine Vielzahl von Argumenten, die spezifische Details zu einer Datenbankspalte angeben, einschließlich Serverspezifikationen und Constraint-Informationen wie die Zugehörigkeit zum Primärschlüssel und Fremdschlüssel. Die mapped_column()-Direktive akzeptiert eine Obermenge von Argumenten, die von der SQLAlchemy-Klasse Column akzeptiert werden, die von SQLAlchemy Core zur Darstellung von Datenbankspalten verwendet wird.
Alle ORM-abgebildeten Klassen erfordern, dass mindestens eine Spalte als Teil des Primärschlüssels deklariert wird, typischerweise durch Verwendung des Parameters Column.primary_key für die mapped_column()-Objekte, die Teil des Schlüssels sein sollen. Im obigen Beispiel sind die Spalten User.id und Address.id als Primärschlüssel markiert.
Zusammenfassend ist die Kombination aus einem String-Tabellennamen und einer Liste von Spaltendeklarationen in SQLAlchemy als Tabellenmetadaten bekannt. Die Einrichtung von Tabellenmetadaten mit beiden Ansätzen, Core und ORM, wird im SQLAlchemy Unified Tutorial unter Arbeiten mit Datenbankmetadaten vorgestellt. Die obige Abbildung ist ein Beispiel für eine sogenannte Annotated Declarative Table-Konfiguration.
Andere Varianten von Mapped sind verfügbar, am häufigsten das relationship()-Konstrukt, das oben gezeigt wurde. Im Gegensatz zu den spaltenbasierten Attributen bezeichnet relationship() eine Verknüpfung zwischen zwei ORM-Klassen. Im obigen Beispiel verknüpft User.addresses User mit Address, und Address.user verknüpft Address mit User. Das relationship()-Konstrukt wird im SQLAlchemy Unified Tutorial unter Arbeiten mit ORM zugehörigen Objekten eingeführt.
Schließlich enthalten die obigen Beispielklassen eine __repr__()-Methode, die nicht erforderlich, aber für das Debugging nützlich ist. Gemappte Klassen können mit Methoden wie __repr__() automatisch generiert werden, indem Data-Klassen verwendet werden. Mehr über Data-Klassen-Abbildungen unter Deklarative Data-Klassen-Abbildung.
Eine Engine erstellen¶
Die Engine ist eine Fabrik, die neue Datenbankverbindungen für uns erstellen kann, die auch Verbindungen innerhalb eines Verbindungspools zur schnellen Wiederverwendung bereithält. Zu Lernzwecken verwenden wir normalerweise eine SQLite-Datenbank nur im Speicher für Bequemlichkeit.
>>> from sqlalchemy import create_engine
>>> engine = create_engine("sqlite://", echo=True)Tipp
Der Parameter echo=True gibt an, dass von Verbindungen ausgegebene SQL auf der Standardausgabe protokolliert wird.
Eine vollständige Einführung in die Engine beginnt unter Konnektivität herstellen - die Engine.
CREATE TABLE DDL ausgeben¶
Mithilfe unserer Tabellenmetadaten und unserer Engine können wir unser Schema auf einmal in unserer Ziel-SQLite-Datenbank generieren, mithilfe einer Methode namens MetaData.create_all()
>>> Base.metadata.create_all(engine)
BEGIN (implicit)
PRAGMA main.table_...info("user_account")
...
PRAGMA main.table_...info("address")
...
CREATE TABLE user_account (
id INTEGER NOT NULL,
name VARCHAR(30) NOT NULL,
fullname VARCHAR,
PRIMARY KEY (id)
)
...
CREATE TABLE address (
id INTEGER NOT NULL,
email_address VARCHAR NOT NULL,
user_id INTEGER NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES user_account (id)
)
...
COMMIT
Viel ist aus diesem kleinen Stück Python-Code passiert, das wir geschrieben haben. Für einen vollständigen Überblick darüber, was mit Tabellenmetadaten vor sich geht, fahren Sie im Tutorial unter Arbeiten mit Datenbankmetadaten fort.
Objekte erstellen und speichern¶
Wir sind nun bereit, Daten in die Datenbank einzufügen. Dies erreichen wir durch Erstellung von Instanzen der Klassen User und Address, die eine __init__()-Methode haben, die bereits automatisch durch den deklarativen Mapping-Prozess etabliert wurde. Wir übergeben sie dann an die Datenbank über ein Objekt namens Session, die die Engine verwendet, um mit der Datenbank zu interagieren. Die Methode Session.add_all() wird hier verwendet, um mehrere Objekte gleichzeitig hinzuzufügen, und die Methode Session.commit() wird verwendet, um alle ausstehenden Änderungen an die Datenbank zu flushen und dann die aktuelle Datenbanktransaktion zu committen, die immer im Gange ist, wenn die Session verwendet wird.
>>> from sqlalchemy.orm import Session
>>> with Session(engine) as session:
... spongebob = User(
... name="spongebob",
... fullname="Spongebob Squarepants",
... addresses=[Address(email_address="spongebob@sqlalchemy.org")],
... )
... sandy = User(
... name="sandy",
... fullname="Sandy Cheeks",
... addresses=[
... Address(email_address="sandy@sqlalchemy.org"),
... Address(email_address="sandy@squirrelpower.org"),
... ],
... )
... patrick = User(name="patrick", fullname="Patrick Star")
...
... session.add_all([spongebob, sandy, patrick])
...
... session.commit()
BEGIN (implicit)
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[...] ('spongebob', 'Spongebob Squarepants')
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[...] ('sandy', 'Sandy Cheeks')
INSERT INTO user_account (name, fullname) VALUES (?, ?) RETURNING id
[...] ('patrick', 'Patrick Star')
INSERT INTO address (email_address, user_id) VALUES (?, ?) RETURNING id
[...] ('spongebob@sqlalchemy.org', 1)
INSERT INTO address (email_address, user_id) VALUES (?, ?) RETURNING id
[...] ('sandy@sqlalchemy.org', 2)
INSERT INTO address (email_address, user_id) VALUES (?, ?) RETURNING id
[...] ('sandy@squirrelpower.org', 2)
COMMIT
Tipp
Es wird empfohlen, die Session im Kontextmanager-Stil wie oben zu verwenden, d. h. mit der Python with:-Anweisung. Das Session-Objekt repräsentiert aktive Datenbankressourcen, daher ist es gut, sicherzustellen, dass es geschlossen wird, wenn eine Reihe von Operationen abgeschlossen ist. Im nächsten Abschnitt behalten wir eine Session zur Veranschaulichung geöffnet.
Grundlagen der Erstellung einer Session finden Sie unter Ausführung mit einer ORM Session und mehr unter Grundlagen der Verwendung einer Session.
Grundlegende Informationen zur Persistenz werden dann unter Einfügen von Zeilen unter Verwendung des ORM Unit of Work-Musters vorgestellt.
Einfaches SELECT¶
Mit einigen Zeilen in der Datenbank hier ist die einfachste Form der Ausgabe einer SELECT-Anweisung zum Laden einiger Objekte. Zum Erstellen von SELECT-Anweisungen verwenden wir die Funktion select(), um ein neues Select-Objekt zu erstellen, das wir dann mit einer Session aufrufen. Die Methode, die sich oft beim Abfragen von ORM-Objekten als nützlich erweist, ist die Methode Session.scalars(), die ein ScalarResult-Objekt zurückgibt, das durch die von uns ausgewählten ORM-Objekte iteriert.
>>> from sqlalchemy import select
>>> session = Session(engine)
>>> stmt = select(User).where(User.name.in_(["spongebob", "sandy"]))
>>> for user in session.scalars(stmt):
... print(user)
BEGIN (implicit)
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name IN (?, ?)
[...] ('spongebob', 'sandy')
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')Die obige Abfrage verwendete auch die Methode Select.where(), um WHERE-Kriterien hinzuzufügen, und verwendete auch die Methode ColumnOperators.in_(), die Teil aller SQLAlchemy-spaltenähnlichen Konstrukte ist, um den SQL IN-Operator zu verwenden.
Mehr Details zur Auswahl von Objekten und einzelnen Spalten finden Sie unter ORM-Entitäten und Spalten auswählen.
SELECT mit JOIN¶
Es ist sehr üblich, gleichzeitig mehrere Tabellen abzufragen, und in SQL ist das JOIN-Schlüsselwort der primäre Weg, wie dies geschieht. Das Select-Konstrukt erstellt Joins mithilfe der Methode Select.join().
>>> stmt = (
... select(Address)
... .join(Address.user)
... .where(User.name == "sandy")
... .where(Address.email_address == "sandy@sqlalchemy.org")
... )
>>> sandy_address = session.scalars(stmt).one()
SELECT address.id, address.email_address, address.user_id
FROM address JOIN user_account ON user_account.id = address.user_id
WHERE user_account.name = ? AND address.email_address = ?
[...] ('sandy', 'sandy@sqlalchemy.org')
>>> sandy_address
Address(id=2, email_address='sandy@sqlalchemy.org')Die obige Abfrage illustriert mehrere WHERE-Kriterien, die automatisch mit AND verkettet sind, sowie wie SQLAlchemy spaltenähnliche Objekte verwendet werden, um „Gleichheits“-Vergleiche zu erstellen, die die überschriebene Python-Methode ColumnOperators.__eq__() verwenden, um ein SQL-Kriterienobjekt zu erzeugen.
Weitere Hintergründe zu den obigen Konzepten finden Sie unter Die WHERE-Klausel und Explizite FROM-Klauseln und JOINs.
Änderungen vornehmen¶
Das Session-Objekt verfolgt zusammen mit unseren ORM-abgebildeten Klassen User und Address automatisch Änderungen an den Objekten, während sie vorgenommen werden, was zu SQL-Anweisungen führt, die bei der nächsten Session.flush() ausgegeben werden. Unten ändern wir eine E-Mail-Adresse, die mit "sandy" verbunden ist, und fügen auch eine neue E-Mail-Adresse zu "patrick" hinzu, nachdem wir ein SELECT ausgegeben haben, um die Zeile für "patrick" abzurufen.
>>> stmt = select(User).where(User.name == "patrick")
>>> patrick = session.scalars(stmt).one()
SELECT user_account.id, user_account.name, user_account.fullname
FROM user_account
WHERE user_account.name = ?
[...] ('patrick',)
>>> patrick.addresses.append(Address(email_address="patrickstar@sqlalchemy.org"))
SELECT address.id AS address_id, address.email_address AS address_email_address, address.user_id AS address_user_id
FROM address
WHERE ? = address.user_id
[...] (3,)
>>> sandy_address.email_address = "sandy_cheeks@sqlalchemy.org"
>>> session.commit()
UPDATE address SET email_address=? WHERE address.id = ?
[...] ('sandy_cheeks@sqlalchemy.org', 2)
INSERT INTO address (email_address, user_id) VALUES (?, ?)
[...] ('patrickstar@sqlalchemy.org', 3)
COMMIT
Beachten Sie, dass beim Zugriff auf patrick.addresses ein SELECT ausgegeben wurde. Dies wird als Lazy Load bezeichnet. Hintergründe zu verschiedenen Möglichkeiten, auf verwandte Elemente mit mehr oder weniger SQL zuzugreifen, werden unter Laderstrategien vorgestellt.
Ein detaillierter Leitfaden zur ORM-Datenmanipulation beginnt unter Datenmanipulation mit dem ORM.
Einige Löschungen¶
Alles hat ein Ende, so auch einige unserer Datenbankzeilen - hier ist eine kurze Demonstration zweier verschiedener Formen der Löschung, die beide je nach spezifischem Anwendungsfall wichtig sind.
Zuerst entfernen wir eines der Address-Objekte des "sandy"-Benutzers. Wenn die Session das nächste Mal flusht, wird dies zum Löschen der Zeile führen. Dieses Verhalten ist etwas, das wir in unserer Abbildung als Löschkaskade konfiguriert haben. Wir können einen Zugriff auf das sandy-Objekt über den Primärschlüssel mit Session.get() erhalten und dann mit dem Objekt arbeiten.
>>> sandy = session.get(User, 2)
BEGIN (implicit)
SELECT user_account.id AS user_account_id, user_account.name AS user_account_name, user_account.fullname AS user_account_fullname
FROM user_account
WHERE user_account.id = ?
[...] (2,)
>>> sandy.addresses.remove(sandy_address)
SELECT address.id AS address_id, address.email_address AS address_email_address, address.user_id AS address_user_id
FROM address
WHERE ? = address.user_id
[...] (2,)
Das letzte SELECT oben war die Lazy Load-Operation, die ausgeführt wurde, damit die Sammlung sandy.addresses geladen werden konnte, damit wir das Mitglied sandy_address entfernen konnten. Es gibt andere Möglichkeiten, diese Reihe von Operationen durchzuführen, die nicht so viel SQL ausgeben.
Wir können wählen, das DELETE SQL für das bisher zu Ändernende auszugeben, ohne die Transaktion zu committen, indem wir die Methode Session.flush() verwenden.
>>> session.flush()
DELETE FROM address WHERE address.id = ?
[...] (2,)
Als nächstes löschen wir den "patrick"-Benutzer vollständig. Für eine top-level Löschung eines Objekts an sich verwenden wir die Methode Session.delete(); diese Methode führt die Löschung nicht tatsächlich durch, sondern bereitet das Objekt für die Löschung beim nächsten Flush vor. Die Operation wird auch kaskadiert auf zugehörige Objekte, basierend auf den von uns konfigurierten Kaskadeoptionen, in diesem Fall auf die zugehörigen Address-Objekte.
>>> session.delete(patrick)
SELECT user_account.id AS user_account_id, user_account.name AS user_account_name, user_account.fullname AS user_account_fullname
FROM user_account
WHERE user_account.id = ?
[...] (3,)
SELECT address.id AS address_id, address.email_address AS address_email_address, address.user_id AS address_user_id
FROM address
WHERE ? = address.user_id
[...] (3,)
Die Methode Session.delete() hat in diesem speziellen Fall zwei SELECT-Anweisungen ausgegeben, obwohl sie kein DELETE ausgegeben hat, was überraschend erscheinen mag. Dies liegt daran, dass die Methode beim Überprüfen des Objekts feststellte, dass das Objekt patrick abgelaufen war, was geschah, als wir zuletzt Session.commit() aufgerufen hatten, und die ausgegebene SQL war, die Zeilen aus der neuen Transaktion neu zu laden. Dieses Ablaufdatum ist optional, und im normalen Gebrauch werden wir es oft für Situationen ausschalten, in denen es nicht gut passt.
Um die zu löschenden Zeilen zu veranschaulichen, hier ist der Commit
>>> session.commit()
DELETE FROM address WHERE address.id = ?
[...] (4,)
DELETE FROM user_account WHERE user_account.id = ?
[...] (3,)
COMMIT
Das Tutorial diskutiert die ORM-Löschung unter ORM-Objekte löschen unter Verwendung des Unit of Work-Musters. Hintergründe zur Objektverfall sind unter Verfall / Aktualisierung; Kaskaden werden ausführlich unter Kaskaden diskutiert.
Lernen Sie die oben genannten Konzepte im Detail¶
Für einen neuen Benutzer waren die obigen Abschnitte wahrscheinlich eine Wirbelwindtour. Es gibt viele wichtige Konzepte in jedem der obigen Schritte, die nicht behandelt wurden. Mit einem schnellen Überblick darüber, wie die Dinge aussehen, wird empfohlen, das SQLAlchemy Unified Tutorial durchzuarbeiten, um ein solides Arbeitswissen darüber zu erlangen, was wirklich vor sich geht. Viel Erfolg!
Die Designs von flambé! dem Drachen und Der Alchemist wurden von Rotem Yaari erstellt und großzügig gespendet.
Erstellt mit Sphinx 7.2.6. Dokumentation zuletzt generiert: Di 11 Mär 2025 14:40:17 EDT