SQLAlchemy 2.0 Dokumentation
SQLAlchemy ORM
- ORM Schnellstart
- ORM Abgebildete Klassenkonfiguration
- Übersicht über ORM-gemappte Klassen
- Klassen mit Deklarativität zuordnen
- Integration mit dataclasses und attrs
- SQL-Ausdrücke als gemappte Attribute¶
- Ändern des Attributverhaltens
- Zusammengesetzte Spaltentypen
- Abbildung von Klassenhierarchien
- Nicht-traditionelle Zuordnungen
- Konfigurieren eines Versionszählers
- Klassen-Mapping-API
- SQL-Ausdrücke zuordnen
- Beziehungskonfiguration
- ORM Abfragehandbuch
- Verwendung der Sitzung
- Ereignisse und Interna
- ORM Erweiterungen
- ORM Beispiele
Projektversionen
- Vorher: Integration mit dataclasses und attrs
- Weiter: Ändern des Attributverhaltens
- Nach oben: Startseite
- Auf dieser Seite
SQL-Ausdrücke als gemappte Attribute¶
Attribute einer gemappten Klasse können mit SQL-Ausdrücken verknüpft werden, die in Abfragen verwendet werden können.
Verwendung eines Hybriden¶
Der einfachste und flexibelste Weg, relativ einfache SQL-Ausdrücke mit einer Klasse zu verknüpfen, ist die Verwendung eines sogenannten „Hybrid-Attributs“, das im Abschnitt Hybrid-Attribute beschrieben wird. Der Hybrid ermöglicht einen Ausdruck, der sowohl auf Python-Ebene als auch auf SQL-Ausdrucksebene funktioniert. Unten ordnen wir zum Beispiel eine Klasse User ab, die die Attribute firstname und lastname enthält, und schließen einen Hybrid ein, der uns den fullname liefert, was die String-Verkettung der beiden ist.
from sqlalchemy.ext.hybrid import hybrid_property
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
firstname = mapped_column(String(50))
lastname = mapped_column(String(50))
@hybrid_property
def fullname(self):
return self.firstname + " " + self.lastnameOben wird das Attribut fullname sowohl auf Instanz- als auch auf Klassenebene interpretiert, sodass es von einer Instanz verfügbar ist.
some_user = session.scalars(select(User).limit(1)).first()
print(some_user.fullname)sowie in Abfragen verwendbar ist.
some_user = session.scalars(
select(User).where(User.fullname == "John Smith").limit(1)
).first()Das Beispiel für die String-Verkettung ist einfach, wobei der Python-Ausdruck auf Instanz- und Klassenebene zweckentfremdet werden kann. Oft muss der SQL-Ausdruck vom Python-Ausdruck unterschieden werden, was durch die Verwendung von hybrid_property.expression() erreicht werden kann. Unten illustrieren wir den Fall, in dem eine Bedingung innerhalb des Hybriden vorhanden sein muss, unter Verwendung der if-Anweisung in Python und der case()-Konstruktion für SQL-Ausdrücke.
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import case
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
firstname = mapped_column(String(50))
lastname = mapped_column(String(50))
@hybrid_property
def fullname(self):
if self.firstname is not None:
return self.firstname + " " + self.lastname
else:
return self.lastname
@fullname.expression
def fullname(cls):
return case(
(cls.firstname != None, cls.firstname + " " + cls.lastname),
else_=cls.lastname,
)Verwendung von column_property¶
Die Funktion column_property() kann verwendet werden, um einen SQL-Ausdruck ähnlich einer regulär zugeordneten Column zuzuordnen. Mit dieser Technik wird das Attribut zusammen mit allen anderen spaltenzugeordneten Attributen zur Ladezeit geladen. Dies ist in einigen Fällen ein Vorteil gegenüber der Verwendung von Hybriden, da der Wert im Voraus zur gleichen Zeit wie die übergeordnete Zeile des Objekts geladen werden kann, insbesondere wenn der Ausdruck eine Verknüpfung zu anderen Tabellen ist (typischerweise als korrelierte Unterabfrage), um Daten abzurufen, die normalerweise nicht auf einem bereits geladenen Objekt verfügbar wären.
Nachteile bei der Verwendung von column_property() für SQL-Ausdrücke sind, dass der Ausdruck mit der für die Klasse als Ganzes ausgegebenen SELECT-Anweisung kompatibel sein muss, und es gibt auch einige Konfigurationsbesonderheiten, die auftreten können, wenn column_property() von deklarativen Mixins verwendet wird.
Unser „fullname“-Beispiel kann mit column_property() wie folgt ausgedrückt werden:
from sqlalchemy.orm import column_property
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
firstname = mapped_column(String(50))
lastname = mapped_column(String(50))
fullname = column_property(firstname + " " + lastname)Korrelierte Unterabfragen können ebenfalls verwendet werden. Unten verwenden wir die select()-Konstruktion, um eine ScalarSelect zu erstellen, die eine spaltenorientierte SELECT-Anweisung darstellt und die Anzahl der Address-Objekte zählt, die für einen bestimmten User verfügbar sind.
from sqlalchemy.orm import column_property
from sqlalchemy import select, func
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import DeclarativeBase
class Base(DeclarativeBase):
pass
class Address(Base):
__tablename__ = "address"
id = mapped_column(Integer, primary_key=True)
user_id = mapped_column(Integer, ForeignKey("user.id"))
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
address_count = column_property(
select(func.count(Address.id))
.where(Address.user_id == id)
.correlate_except(Address)
.scalar_subquery()
)Im obigen Beispiel definieren wir eine ScalarSelect()-Konstruktion wie folgt:
stmt = (
select(func.count(Address.id))
.where(Address.user_id == id)
.correlate_except(Address)
.scalar_subquery()
)Oben verwenden wir zuerst select(), um eine Select-Konstruktion zu erstellen, die wir dann mit der Methode Select.scalar_subquery() in eine Skalarunterabfrage umwandeln, was unsere Absicht anzeigt, diese Select-Anweisung im Kontext eines Spaltenausdrucks zu verwenden.
Innerhalb der Select wählen wir die Anzahl der Address.id-Zeilen aus, bei denen die Spalte Address.user_id mit id gleichgesetzt ist, was im Kontext der User-Klasse die Column mit dem Namen id ist (beachten Sie, dass id auch der Name einer Python-eingebauten Funktion ist, die wir hier nicht verwenden wollen – wenn wir uns außerhalb der User-Klassendefinition befänden, würden wir User.id verwenden).
Die Methode Select.correlate_except() gibt an, dass jedes Element in der FROM-Klausel dieser select()-Anweisung aus der FROM-Liste weggelassen werden kann (d. h. mit der umschließenden SELECT-Anweisung gegen User korreliert) außer derjenigen, die Address entspricht. Dies ist nicht unbedingt erforderlich, verhindert aber, dass Address versehentlich aus der FROM-Liste ausgeschlossen wird, falls es eine lange Zeichenkette von Joins zwischen User- und Address-Tabellen gibt, bei denen SELECT-Anweisungen gegen Address verschachtelt sind.
Für eine column_property(), die sich auf Spalten bezieht, die aus einer Many-to-Many-Beziehung stammen, verwenden Sie and_(), um die Felder der Assoziationstabelle mit beiden Tabellen in einer Beziehung zu verbinden.
from sqlalchemy import and_
class Author(Base):
# ...
book_count = column_property(
select(func.count(books.c.id))
.where(
and_(
book_authors.c.author_id == authors.c.id,
book_authors.c.book_id == books.c.id,
)
)
.scalar_subquery()
)Hinzufügen von column_property() zu einer bestehenden deklarativ gemappten Klasse¶
Wenn Importprobleme verhindern, dass column_property() inline mit der Klasse definiert wird, kann sie der Klasse nach der Konfiguration beider zugewiesen werden. Bei Verwendung von Zuordnungen, die eine deklarative Basisklasse verwenden (d. h. produziert von der DeclarativeBase Oberklasse oder Legacy-Funktionen wie declarative_base()), hat diese Attributzuweisung die Wirkung des Aufrufs von Mapper.add_property(), um nachträglich eine zusätzliche Eigenschaft hinzuzufügen.
# only works if a declarative base class is in use
User.address_count = column_property(
select(func.count(Address.id)).where(Address.user_id == User.id).scalar_subquery()
)Bei Verwendung von Zuordnungsstilen, die keine deklarativen Basisklassen verwenden, wie z. B. der registry.mapped() Dekorator, kann die Methode Mapper.add_property() explizit auf dem zugrunde liegenden Mapper-Objekt aufgerufen werden, das mit inspect() abgerufen werden kann.
from sqlalchemy.orm import registry
reg = registry()
@reg.mapped
class User:
__tablename__ = "user"
# ... additional mapping directives
# later ...
# works for any kind of mapping
from sqlalchemy import inspect
inspect(User).add_property(
column_property(
select(func.count(Address.id))
.where(Address.user_id == User.id)
.scalar_subquery()
)
)Zusammensetzung aus Spalteneigenschaften zur Zuordnungszeit¶
Es ist möglich, Zuordnungen zu erstellen, die mehrere ColumnProperty-Objekte kombinieren. Das ColumnProperty wird als SQL-Ausdruck interpretiert, wenn es in einem Core-Ausdruckskontext verwendet wird, vorausgesetzt, es wird von einem vorhandenen Ausdrucksobjekt angesprochen; dies geschieht, indem der Core erkennt, dass das Objekt eine Methode __clause_element__() hat, die einen SQL-Ausdruck zurückgibt. Wenn jedoch die ColumnProperty als führendes Objekt in einem Ausdruck verwendet wird, für das es kein anderes Core-SQL-Ausdrucksobjekt gibt, gibt das Attribut ColumnProperty.expression den zugrunde liegenden SQL-Ausdruck zurück, damit er zum konsistenten Aufbau von SQL-Ausdrücken verwendet werden kann. Unten enthält die Klasse File ein Attribut File.path, das einen String-Token an das Attribut File.filename anhängt, welches selbst eine ColumnProperty ist.
class File(Base):
__tablename__ = "file"
id = mapped_column(Integer, primary_key=True)
name = mapped_column(String(64))
extension = mapped_column(String(8))
filename = column_property(name + "." + extension)
path = column_property("C:/" + filename.expression)Wenn die Klasse File normal in Ausdrücken verwendet wird, sind die den Attributen filename und path zugewiesenen Attribute direkt verwendbar. Die Verwendung des Attributs ColumnProperty.expression ist nur erforderlich, wenn die ColumnProperty direkt innerhalb der Zuordnungsdefinition verwendet wird.
stmt = select(File.path).where(File.filename == "foo.txt")Verwendung von Spaltendeferral mit column_property()¶
Das Spaltendeferral-Feature, das im ORM Querying Guide unter Begrenzen, welche Spalten mit Spaltendeferral geladen werden eingeführt wurde, kann zur Zuordnungszeit auf einen SQL-Ausdruck angewendet werden, der von column_property() zugeordnet wird, indem die Funktion deferred() anstelle von column_property() verwendet wird.
from sqlalchemy.orm import deferred
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
firstname: Mapped[str] = mapped_column()
lastname: Mapped[str] = mapped_column()
fullname: Mapped[str] = deferred(firstname + " " + lastname)Verwendung eines einfachen Deskriptors¶
In Fällen, in denen eine komplexere SQL-Abfrage als das, was column_property() oder hybrid_property liefern kann, ausgegeben werden muss, kann eine reguläre Python-Funktion, die als Attribut aufgerufen wird, verwendet werden, vorausgesetzt, der Ausdruck muss nur auf einer bereits geladenen Instanz verfügbar sein. Die Funktion wird mit dem eigenen @property-Decorator von Python dekoriert, um sie als schreibgeschütztes Attribut zu markieren. Innerhalb der Funktion wird object_session() verwendet, um die Session zu finden, die zum aktuellen Objekt gehört, welche dann verwendet wird, um eine Abfrage auszugeben.
from sqlalchemy.orm import object_session
from sqlalchemy import select, func
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
firstname = mapped_column(String(50))
lastname = mapped_column(String(50))
@property
def address_count(self):
return object_session(self).scalar(
select(func.count(Address.id)).where(Address.user_id == self.id)
)Der einfache Deskriptor-Ansatz ist als letzte Möglichkeit nützlich, aber im üblichen Fall weniger performant als sowohl der Hybrid- als auch der Spalten-Eigenschaftsansatz, da er bei jedem Zugriff eine SQL-Abfrage ausgeben muss.
SQL-Ausdrücke zur Abfragezeit als gemappte Attribute¶
Zusätzlich zur Konfiguration fester SQL-Ausdrücke für gemappte Klassen enthält das SQLAlchemy ORM auch eine Funktion, mit der Objekte mit den Ergebnissen beliebiger SQL-Ausdrücke geladen werden können, die zur Abfragezeit als Teil ihres Zustands konfiguriert werden. Dieses Verhalten ist verfügbar, indem ein ORM-gemapptes Attribut mit query_expression() konfiguriert und dann die with_expression() Ladeoption zur Abfragezeit verwendet wird. Siehe den Abschnitt Laden beliebiger SQL-Ausdrücke auf Objekte für ein Beispiel für Zuordnung und Verwendung.
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