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
- Änderung 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: SQL-Ausdrücke als zugeordnete Attribute
- Nächste: Zusammengesetzte Spaltentypen
- Nach oben: Startseite
- Auf dieser Seite
Änderung des Attributverhaltens¶
Dieser Abschnitt behandelt Funktionen und Techniken zur Modifikation des Verhaltens von ORM-zugeordneten Attributen, einschließlich derjenigen, die mit mapped_column(), relationship() und anderen zugeordnet wurden.
Einfache Validatoren¶
Eine schnelle Möglichkeit, eine "Validierungs"-Routine zu einem Attribut hinzuzufügen, ist die Verwendung des validates()-Dekorators. Ein Attribut-Validator kann eine Ausnahme auslösen, die den Prozess der Mutation des Attributwerts stoppt, oder den gegebenen Wert in etwas anderes ändern. Validatoren werden, wie alle Attributerweiterungen, nur vom normalen Benutzer-Code aufgerufen; sie werden nicht ausgegeben, wenn der ORM das Objekt befüllt.
from sqlalchemy.orm import validates
class EmailAddress(Base):
__tablename__ = "address"
id = mapped_column(Integer, primary_key=True)
email = mapped_column(String)
@validates("email")
def validate_email(self, key, address):
if "@" not in address:
raise ValueError("failed simple email validation")
return addressValidatoren empfangen auch Ereignisse beim Hinzufügen von Elementen zu einer Sammlung.
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates("addresses")
def validate_address(self, key, address):
if "@" not in address.email:
raise ValueError("failed simplified email validation")
return addressDie Validierungsfunktion wird standardmäßig nicht für Ereignisse beim Entfernen aus Sammlungen ausgegeben, da die typische Erwartung ist, dass ein verworfener Wert keine Validierung erfordert. validates() unterstützt jedoch den Empfang dieser Ereignisse durch Angabe von include_removes=True im Dekorator. Wenn dieses Flag gesetzt ist, muss die Validierungsfunktion ein zusätzliches boolesches Argument empfangen, das, wenn es True ist, angibt, dass die Operation eine Entfernung ist.
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates("addresses", include_removes=True)
def validate_address(self, key, address, is_remove):
if is_remove:
raise ValueError("not allowed to remove items from the collection")
else:
if "@" not in address.email:
raise ValueError("failed simplified email validation")
return addressDer Fall, bei dem sich gegenseitig abhängige Validatoren über eine Rückreferenz verknüpft sind, kann ebenfalls angepasst werden, indem die Option include_backrefs=False verwendet wird; diese Option verhindert, wenn sie auf False gesetzt ist, dass eine Validierungsfunktion ausgelöst wird, wenn das Ereignis als Ergebnis einer Rückreferenz auftritt.
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address", backref="user")
@validates("addresses", include_backrefs=False)
def validate_address(self, key, address):
if "@" not in address:
raise ValueError("failed simplified email validation")
return addressOben würde, wenn wir Address.user zuweisen, wie in some_address.user = some_user, die Funktion validate_address() *nicht* ausgelöst werden, obwohl ein Hinzufügen zu some_user.addresses stattfindet – das Ereignis wird durch eine Rückreferenz verursacht.
Beachten Sie, dass der validates()-Dekorator eine Komfortfunktion ist, die auf Attributereignissen aufbaut. Eine Anwendung, die mehr Kontrolle über die Konfiguration des Attributänderungsverhaltens benötigt, kann dieses System nutzen, das unter AttributeEvents beschrieben wird.
| Objektname | Beschreibung |
|---|---|
validates(*namen, [include_removes, include_backrefs]) |
Dekoriert eine Methode als „Validator“ für eine oder mehrere benannte Eigenschaften. |
- Funktion sqlalchemy.orm.validates(*namen: str, include_removes: bool = False, include_backrefs: bool = True) → Callable[[_Fn], _Fn]¶
Dekoriert eine Methode als „Validator“ für eine oder mehrere benannte Eigenschaften.
Bezeichnet eine Methode als Validator, eine Methode, die den Namen des Attributs sowie einen zuzuweisenden Wert empfängt oder im Falle einer Sammlung den hinzuzufügenden Wert empfängt. Die Funktion kann dann Validierungsfehler auslösen, um den Fortgang zu stoppen (wobei die integrierten Python-
ValueErrorundAssertionErrorAusnahmen angemessene Wahlmöglichkeiten sind), oder sie kann den Wert modifizieren oder ersetzen, bevor sie fortfährt. Die Funktion sollte andernfalls den gegebenen Wert zurückgeben.Beachten Sie, dass ein Validator für eine Sammlung *nicht* eine Ladung dieser Sammlung innerhalb der Validierungsroutine auslösen kann – diese Verwendung löst eine Assertion aus, um Rekursionsüberläufe zu vermeiden. Dies ist eine reentrant Bedingung, die nicht unterstützt wird.
- Parameter:
*namen¶ – Liste der zu validierenden Attributnamen.
include_removes¶ – Wenn True, werden auch „Entfernungs“-Ereignisse gesendet – die Validierungsfunktion muss ein zusätzliches Argument „is_remove“ akzeptieren, das ein boolescher Wert ist.
include_backrefs¶ –
Standardmäßig
True; wennFalse, wird die Validierungsfunktion nicht ausgelöst, wenn der Ursprung ein Attributereignis ist, das über eine Rückreferenz verbunden ist. Dies kann für bidirektionalevalidates()-Nutzung verwendet werden, bei der nur ein Validator pro Attributoperation ausgelöst werden soll.Geändert in Version 2.0.16: Dieser Parameter hatte versehentlich den Standardwert
Falsefür die Versionen 2.0.0 bis 2.0.15. Sein korrekter Standardwert vonTruewird in 2.0.16 wiederhergestellt.
Siehe auch
Einfache Validatoren - Anwendungsbeispiele für
validates()
Verwendung benutzerdefinierter Datentypen auf Core-Ebene¶
Eine Nicht-ORM-Methode zur Beeinflussung des Werts einer Spalte, um Daten zwischen der Darstellung in Python und der Darstellung in der Datenbank zu konvertieren, kann durch die Verwendung eines benutzerdefinierten Datentyps erreicht werden, der auf die zugeordnete Table-Metadaten angewendet wird. Dies ist häufiger bei bestimmten Arten von Kodierung/Dekodierung der Fall, die sowohl beim Senden von Daten an die Datenbank als auch beim Zurückgeben erfolgen; lesen Sie mehr darüber in der Core-Dokumentation unter Erweiterung bestehender Typen.
Verwendung von Deskriptoren und Hybriden¶
Eine umfassendere Methode zur Erzeugung modifizierten Verhaltens für ein Attribut ist die Verwendung von Deskriptoren. Diese werden in Python üblicherweise mit der Funktion property() verwendet. Die Standard-SQLAlchemy-Technik für Deskriptoren besteht darin, einen einfachen Deskriptor zu erstellen und diesen von einem zugeordneten Attribut mit einem anderen Namen lesen/schreiben zu lassen. Unten illustrieren wir dies mit Eigenschaften im Stil von Python 2.6.
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
# name the attribute with an underscore,
# different from the column name
_email = mapped_column("email", String)
# then create an ".email" attribute
# to get/set "._email"
@property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = emailDer obige Ansatz funktioniert, aber wir können noch mehr hinzufügen. Während unser EmailAddress-Objekt den Wert über den email-Deskriptor und in das _email-zugeordnete Attribut weiterleitet, hat das EmailAddress.email-Attribut auf Klassenebene nicht die üblichen Ausdruckssemantik, die mit Select verwendbar sind. Um diese bereitzustellen, verwenden wir stattdessen die hybrid-Erweiterung wie folgt:
from sqlalchemy.ext.hybrid import hybrid_property
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
_email = mapped_column("email", String)
@hybrid_property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = emailDas Attribut .email bietet nicht nur Getter-/Setter-Verhalten, wenn wir eine Instanz von EmailAddress haben, sondern bietet auch einen SQL-Ausdruck, wenn es auf Klassenebene verwendet wird, d. h. direkt von der Klasse EmailAddress.
from sqlalchemy.orm import Session
from sqlalchemy import select
session = Session()
address = session.scalars(
select(EmailAddress).where(EmailAddress.email == "address@example.com")
).one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE address.email = ?
('address@example.com',)
address.email = "otheraddress@example.com"
session.commit()
UPDATE address SET email=? WHERE address.id = ?
('otheraddress@example.com', 1)
COMMIT
Die hybrid_property ermöglicht es uns auch, das Verhalten des Attributs zu ändern, einschließlich der Definition separater Verhaltensweisen, wenn das Attribut auf Instanzebene im Gegensatz zur Klassen-/Ausdrucksebene aufgerufen wird, unter Verwendung des Modifikators hybrid_property.expression(). Wenn wir beispielsweise automatisch einen Hostnamen hinzufügen möchten, könnten wir zwei Sätze von String-Manipulationslogiken definieren:
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
_email = mapped_column("email", String)
@hybrid_property
def email(self):
"""Return the value of _email up until the last twelve
characters."""
return self._email[:-12]
@email.setter
def email(self, email):
"""Set the value of _email, tacking on the twelve character
value @example.com."""
self._email = email + "@example.com"
@email.expression
def email(cls):
"""Produce a SQL expression that represents the value
of the _email column, minus the last twelve characters."""
return func.substr(cls._email, 0, func.length(cls._email) - 12)Oben gibt der Zugriff auf die email-Eigenschaft einer Instanz von EmailAddress den Wert des _email-Attributs zurück und entfernt oder fügt den Hostnamen @example.com aus dem Wert hinzu oder entfernt ihn. Wenn wir gegen das email-Attribut abfragen, wird eine SQL-Funktion gerendert, die dasselbe bewirkt.
address = session.scalars(
select(EmailAddress).where(EmailAddress.email == "address")
).one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE substr(address.email, ?, length(address.email) - ?) = ?
(0, 12, 'address')
Lesen Sie mehr über Hybride unter Hybride Attribute.
Synonyme¶
Synonyme sind ein Mapper-Konstrukt, das es jedem Attribut einer Klasse ermöglicht, ein anderes zugeordnetes Attribut zu "spiegeln".
Im Grunde ist das Synonym eine einfache Möglichkeit, ein bestimmtes Attribut unter einem zusätzlichen Namen verfügbar zu machen.
from sqlalchemy.orm import synonym
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
job_status = mapped_column(String(50))
status = synonym("job_status")Die obige Klasse MyClass hat zwei Attribute, .job_status und .status, die sich wie ein Attribut verhalten werden, sowohl auf Ausdrucksebene.
>>> print(MyClass.job_status == "some_status")
my_table.job_status = :job_status_1
>>> print(MyClass.status == "some_status")
my_table.job_status = :job_status_1
als auch auf Instanzebene.
>>> m1 = MyClass(status="x")
>>> m1.status, m1.job_status
('x', 'x')
>>> m1.job_status = "y"
>>> m1.status, m1.job_status
('y', 'y')Die synonym() kann für jede Art von zugeordnetem Attribut verwendet werden, das von MapperProperty erbt, einschließlich zugeordneter Spalten und Beziehungen, sowie Synonyme selbst.
Über ein einfaches Spiegeln hinaus kann synonym() auch auf einen vom Benutzer definierten Deskriptor verweisen. Wir können unserem status-Synonym eine @property zuweisen.
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
status = mapped_column(String(50))
@property
def job_status(self):
return "Status: " + self.status
job_status = synonym("status", descriptor=job_status)Bei der Verwendung von Declarative kann das obige Muster mithilfe des synonym_for()-Dekorators kürzer ausgedrückt werden.
from sqlalchemy.ext.declarative import synonym_for
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
status = mapped_column(String(50))
@synonym_for("status")
@property
def job_status(self):
return "Status: " + self.statusWährend synonym() für einfaches Spiegeln nützlich ist, wird der Anwendungsfall der Erweiterung des Attributverhaltens mit Deskriptoren im modernen Gebrauch besser durch die Funktion für hybride Attribute gehandhabt, die stärker auf Python-Deskriptoren ausgerichtet ist. Technisch gesehen kann ein synonym() alles tun, was eine hybrid_property kann, da es auch die Injektion benutzerdefinierter SQL-Funktionen unterstützt, aber das Hybrid ist in komplexeren Situationen einfacher zu verwenden.
| Objektname | Beschreibung |
|---|---|
synonym(name, *, [map_column, descriptor, comparator_factory, init, repr, default, default_factory, compare, kw_only, hash, info, doc]) |
Bezeichnet einen Attributnamen als Synonym für eine zugeordnete Eigenschaft, sodass das Attribut das Wert- und Ausdrucksverhalten eines anderen Attributs widerspiegelt. |
- Funktion sqlalchemy.orm.synonym(name: str, *, map_column: bool | None = None, descriptor: Any | None = None, comparator_factory: Type[PropComparator[_T]] | None = None, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: _NoArg | _T = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, info: _InfoType | None = None, doc: str | None = None) → Synonym[Any]¶
Bezeichnet einen Attributnamen als Synonym für eine zugeordnete Eigenschaft, sodass das Attribut das Wert- und Ausdrucksverhalten eines anderen Attributs widerspiegelt.
z. B.
class MyClass(Base): __tablename__ = "my_table" id = Column(Integer, primary_key=True) job_status = Column(String(50)) status = synonym("job_status")
- Parameter:
name¶ – Der Name der vorhandenen zugeordneten Eigenschaft. Dies kann sich auf den String-Namen eines ORM-zugeordneten Attributs auf der Klasse beziehen, einschließlich spaltengebundener Attribute und Beziehungen.
descriptor¶ – Ein Python-Deskriptor, der als Getter (und potenziell als Setter) verwendet wird, wenn auf dieses Attribut auf Instanzebene zugegriffen wird.
map_column¶ –
Nur für klassische Zuordnungen und Zuordnungen zu einem vorhandenen Table-Objekt. Wenn
True, sucht diesynonym()-Konstruktion dieColumnauf der zugeordneten Tabelle, die normalerweise mit dem Attributnamen dieses Synonyms verbunden wäre, und erzeugt eine neueColumnProperty, die stattdessen dieseColumndem alternativen Namen zuordnet, der als „name“-Argument des Synonyms angegeben wurde; auf diese Weise ist der übliche Schritt, die Zuordnung derColumnzu einem anderen Namen neu zu definieren, nicht mehr notwendig. Dies ist normalerweise beabsichtigt, wenn eineColumndurch ein Attribut ersetzt werden soll, das ebenfalls einen Deskriptor verwendet, d. h. in Verbindung mit dem Parametersynonym.descriptor.my_table = Table( "my_table", metadata, Column("id", Integer, primary_key=True), Column("job_status", String(50)), ) class MyClass: @property def _job_status_descriptor(self): return "Status: %s" % self._job_status mapper( MyClass, my_table, properties={ "job_status": synonym( "_job_status", map_column=True, descriptor=MyClass._job_status_descriptor, ) }, )
Oben wird das Attribut mit dem Namen
_job_statusautomatisch der Spaltejob_statuszugeordnet.>>> j1 = MyClass() >>> j1._job_status = "employed" >>> j1.job_status Status: employed
Bei der Verwendung von Declarative, um einen Deskriptor in Verbindung mit einem Synonym bereitzustellen, verwenden Sie die Hilfsfunktion
sqlalchemy.ext.declarative.synonym_for(). Beachten Sie jedoch, dass die Funktion für hybride Eigenschaften in der Regel bevorzugt werden sollte, insbesondere bei der Neudefinition des Attributverhaltens.info¶ – Optionales Datenwörterbuch, das in das Attribut
InspectionAttr.infodieses Objekts aufgenommen wird.comparator_factory¶ –
Eine Unterklasse von
PropComparator, die ein benutzerdefiniertes Vergleichsverhalten auf SQL-Ausdrucksebene bereitstellt.Hinweis
Für den Anwendungsfall, ein Attribut bereitzustellen, das sowohl das Verhalten auf Python-Ebene als auch das Verhalten auf SQL-Ausdrucksebene eines Attributs neu definiert, beachten Sie bitte die unter Verwendung von Deskriptoren und Hybriden eingeführte Hybrid-Attributfunktion als effektivere Technik.
Siehe auch
Synonyme - Überblick über Synonyme
synonym_for()- ein Helfer für DeclarativeVerwendung von Deskriptoren und Hybriden - Die Hybrid-Attributerweiterung bietet einen aktualisierten Ansatz zur Erweiterung des Attributverhaltens, der flexibler ist als mit Synonymen erreichbar.
Operator-Anpassung¶
Die "Operatoren", die von der SQLAlchemy ORM und der Core Expression Language verwendet werden, sind vollständig anpassbar. Zum Beispiel macht der Vergleichsausdruck User.name == 'ed' Gebrauch von einem in Python selbst eingebauten Operator namens operator.eq – die tatsächliche SQL-Konstruktion, die SQLAlchemy mit einem solchen Operator assoziiert, kann modifiziert werden. Neue Operationen können auch Spaltenausdrücken zugeordnet werden. Die Operatoren, die für Spaltenausdrücke gelten, werden am direktesten auf Typenebene neu definiert – siehe Abschnitt Neudefinition und Erstellung neuer Operatoren für eine Beschreibung.
ORM-Funktionen wie column_property(), relationship() und composite() bieten ebenfalls die Neudefinition von Operatoren auf ORM-Ebene, indem eine Unterklasse von PropComparator an das Argument comparator_factory jeder Funktion übergeben wird. Die Anpassung von Operatoren auf dieser Ebene ist ein seltener Anwendungsfall. Siehe die Dokumentation unter PropComparator für einen Überblick.
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