SQLAlchemy 2.0 Dokumentation
Änderungen und Migration
- SQLAlchemy 2.0 - Major Migration Guide
- Was ist neu in SQLAlchemy 2.0?
- 2.0 Changelog
- 1.4 Changelog
- 1.3 Changelog
- 1.2 Changelog
- 1.1 Changelog
- 1.0 Changelog
- 0.9 Changelog
- 0.8 Changelog
- 0.7 Changelog
- 0.6 Changelog
- 0.5 Changelog
- 0.4 Changelog
- 0.3 Changelog
- 0.2 Changelog
- 0.1 Changelog
- Was ist neu in SQLAlchemy 1.4?
- Was ist neu in SQLAlchemy 1.3?
- Was ist neu in SQLAlchemy 1.2?
- Was ist neu in SQLAlchemy 1.1?
- Was ist neu in SQLAlchemy 1.0?
- Was ist neu in SQLAlchemy 0.9?
- Was ist neu in SQLAlchemy 0.8?
- Was ist neu in SQLAlchemy 0.7?
- Was gibt es Neues in SQLAlchemy 0.6?¶
- Plattformunterstützung
- Neues Dialektsystem
- Änderungen an der Expression Language
- C-Erweiterungen für das Abrufen von Ergebnissen
- Neue Schemafunktionen
- Logging wurde geöffnet
- Reflection/Inspector API
- RETURNING-Unterstützung
- Änderungen am Typsystem
- ORM-Änderungen
- Neues Unit of Work
- Änderungen an
query.update()undquery.delete() relation()heißt jetzt offiziellrelationship()- Subquery Eager Loading
`eagerload()``,``eagerload_all()``sind jetzt``joinedload()``,``joinedload_all()``lazy=False|None|True|'dynamic'``akzeptiert jetzt``lazy='noload'|'joined'|'subquery'|'select'|'dynamic'`- innerjoin=True bei relation, joinedload
- Verbesserungen bei Many-to-one
- Veränderbare Primärschlüssel mit Joined Table Inheritance
- Beaker Caching
- Weitere Änderungen
- Veraltete/Entfernte ORM-Elemente
- Erweiterungen
- Was ist neu in SQLAlchemy 0.5?
- Was ist neu in SQLAlchemy 0.4?
Projektversionen
- Vorher: Was gibt es Neues in SQLAlchemy 0.7?
- Nächste: Was gibt es Neues in SQLAlchemy 0.5?
- Nach oben: Startseite
- Auf dieser Seite
- Was ist neu in SQLAlchemy 0.6?
- Plattformunterstützung
- Neues Dialektsystem
- Änderungen an der Expression Language
- C-Erweiterungen für das Abrufen von Ergebnissen
- Neue Schemafunktionen
- Logging wurde geöffnet
- Reflection/Inspector API
- RETURNING-Unterstützung
- Änderungen am Typsystem
- ORM-Änderungen
- Neues Unit of Work
- Änderungen an
query.update()undquery.delete() relation()heißt jetzt offiziellrelationship()- Subquery Eager Loading
`eagerload()``,``eagerload_all()``sind jetzt``joinedload()``,``joinedload_all()``lazy=False|None|True|'dynamic'``akzeptiert jetzt``lazy='noload'|'joined'|'subquery'|'select'|'dynamic'`- innerjoin=True bei relation, joinedload
- Verbesserungen bei Many-to-one
- Veränderbare Primärschlüssel mit Joined Table Inheritance
- Beaker Caching
- Weitere Änderungen
- Veraltete/Entfernte ORM-Elemente
- Erweiterungen
Was gibt es Neues in SQLAlchemy 0.6?¶
Über dieses Dokument
Dieses Dokument beschreibt die Änderungen zwischen SQLAlchemy Version 0.5, zuletzt veröffentlicht am 16. Januar 2010, und SQLAlchemy Version 0.6, zuletzt veröffentlicht am 5. Mai 2012.
Dokumentdatum: 6. Juni 2010
Diese Anleitung dokumentiert API-Änderungen, die Benutzer betreffen, die ihre Anwendungen von der 0.5er Serie von SQLAlchemy auf 0.6 migrieren. Beachten Sie, dass SQLAlchemy 0.6 einige Verhaltensweisen entfernt, die während der gesamten 0.5er Serie als veraltet markiert waren, und weitere spezifische Verhaltensweisen für 0.5 als veraltet markiert.
Plattformunterstützung¶
cPython-Versionen 2.4 und aufwärts in der 2.xx-Serie
Jython 2.5.1 - mit dem zxJDBC DBAPI, der mit Jython geliefert wird.
cPython 3.x - siehe [source:sqlalchemy/trunk/README.py3k] für Informationen zum Erstellen für python3.
Neues Dialektsystem¶
Dialektmodule sind nun in verschiedene Unterkomponenten aufgeteilt, innerhalb des Rahmens eines einzelnen Datenbank-Backends. Dialektimplementierungen befinden sich nun im Paket sqlalchemy.dialects. Das Paket sqlalchemy.databases existiert weiterhin als Platzhalter, um ein gewisses Maß an Abwärtskompatibilität für einfache Importe zu gewährleisten.
Für jede unterstützte Datenbank gibt es ein Unterpaket innerhalb von sqlalchemy.dialects, das mehrere Dateien enthält. Jedes Paket enthält ein Modul namens base.py, das den spezifischen SQL-Dialekt für diese Datenbank definiert. Es enthält auch ein oder mehrere „Treiber“-Module, die jeweils einem bestimmten DBAPI entsprechen – diese Dateien sind nach dem DBAPI benannt, wie z.B. pysqlite, cx_oracle oder pyodbc. Die von SQLAlchemy-Dialekten verwendeten Klassen werden zuerst im Modul base.py deklariert und definieren alle Verhaltensmerkmale, die von der Datenbank definiert werden. Dazu gehören Fähigkeitszuordnungen, wie z.B. „unterstützt Sequenzen“, „unterstützt Rückgabewerte“ usw., Typdefinitionen und SQL-Kompilierungsregeln. Jedes „Treiber“-Modul wiederum stellt bei Bedarf Unterklassen dieser Klassen bereit, die das Standardverhalten überschreiben, um die zusätzlichen Funktionen, Verhaltensweisen und Eigenheiten dieses DBAPI zu berücksichtigen. Für DBAPIs, die mehrere Backends unterstützen (pyodbc, zxJDBC, mxODBC), verwendet das Dialektmodul Mixins aus dem Paket sqlalchemy.connectors, die Funktionalität bereitstellen, die für diesen DBAPI über alle Backends hinweg gemeinsam ist, insbesondere die Behandlung von Verbindungsargumenten. Das bedeutet, dass die Verbindung über pyodbc, zxJDBC oder mxODBC (wenn implementiert) über unterstützte Backends hinweg äußerst konsistent ist.
Das URL-Format, das von create_engine() verwendet wird, wurde erweitert, um eine beliebige Anzahl von DBAPIs für ein bestimmtes Backend zu handhaben, wobei ein Schema verwendet wird, das von dem von JDBC inspiriert ist. Das vorherige Format funktioniert weiterhin und wählt eine „Standard“-DBAPI-Implementierung aus, wie z.B. die untenstehende PostgreSQL-URL, die psycopg2 verwendet.
create_engine("postgresql://scott:tiger@localhost/test")Um jedoch ein bestimmtes DBAPI-Backend wie pg8000 anzugeben, fügen Sie es mit einem Pluszeichen „+“ dem „Protokoll“-Teil der URL hinzu.
create_engine("postgresql+pg8000://scott:tiger@localhost/test")Wichtige Dialektlinks
Dokumentation zu Verbindungsargumenten: https://sqlalchemy.de/docs/06/dbengine.html#create-engine-url-arguments.
Referenzdokumentation für einzelne Dialekte: https://sqlalchemy.de/docs/06/reference/dialects/index.html.
Die Tipps und Tricks unter DatabaseNotes.
Weitere Hinweise zu Dialekten
Das Typsystem wurde in SQLAlchemy 0.6 drastisch geändert. Dies hat Auswirkungen auf alle Dialekte in Bezug auf Namenskonventionen, Verhaltensweisen und Implementierungen. Siehe den Abschnitt „Typen“ unten.
Das
ResultProxy-Objekt bietet dank einiger Refactorings in einigen Fällen eine doppelt so hohe Geschwindigkeit.Das
RowProxy, d.h. ein einzelnes Ergebniszeilenobjekt, ist nun direkt picklebar.Der Setuptools-Entrypoint, der zum Auffinden externer Dialekte verwendet wird, heißt jetzt
sqlalchemy.dialects. Ein externer Dialekt, der für 0.4 oder 0.5 geschrieben wurde, muss in jedem Fall modifiziert werden, um mit 0.6 zu funktionieren. Diese Änderung fügt also keine zusätzlichen Schwierigkeiten hinzu.Dialekte erhalten nun ein initialize()-Ereignis bei der ersten Verbindung, um Verbindungseigenschaften zu ermitteln.
Von der Compiler generierte Funktionen und Operatoren verwenden nun (fast) reguläre Dispatch-Funktionen der Form „visit_<opname>“ und „visit_<funcname>_fn“, um eine benutzerdefinierte Verarbeitung zu ermöglichen. Dies ersetzt die Notwendigkeit, die Wörterbücher „functions“ und „operators“ in Compiler-Unterklassen durch einfache Visitor-Methoden zu kopieren, und ermöglicht es Compiler-Unterklassen auch, die vollständige Kontrolle über das Rendering zu haben, da das vollständige _Function- oder _BinaryExpression-Objekt übergeben wird.
Dialektimporte¶
Die Importstruktur von Dialekten hat sich geändert. Jeder Dialekt exportiert nun seine Basis-„Dialekt“-Klasse sowie den vollständigen Satz der auf diesem Dialekt unterstützten SQL-Typen über sqlalchemy.dialects.<name>. Zum Beispiel, um eine Menge von PG-Typen zu importieren.
from sqlalchemy.dialects.postgresql import (
INTEGER,
BIGINT,
SMALLINT,
VARCHAR,
MACADDR,
DATE,
BYTEA,
)Oben ist INTEGER tatsächlich der einfache INTEGER-Typ aus sqlalchemy.types, aber der PG-Dialekt macht ihn auf die gleiche Weise verfügbar wie die Typen, die spezifisch für PG sind, wie z.B. BYTEA und MACADDR.
Änderungen an der Expression Language¶
Ein wichtiges Stolpersteinchen der Expression Language¶
Es gibt eine ziemlich signifikante Verhaltensänderung an der Expression Language, die einige Anwendungen betreffen kann. Der boolesche Wert von Python-Booleschen Ausdrücken, d.h. ==, != und ähnliche, wird nun korrekt in Bezug auf die beiden verglichenen Klauselobjekte ausgewertet.
Wie wir wissen, gibt das Vergleichen eines ClauseElement mit jedem anderen Objekt ein weiteres ClauseElement zurück.
>>> from sqlalchemy.sql import column
>>> column("foo") == 5
<sqlalchemy.sql.expression._BinaryExpression object at 0x1252490>Dies geschieht, damit Python-Ausdrücke beim Konvertieren in Strings SQL-Ausdrücke erzeugen.
>>> str(column("foo") == 5)
'foo = :foo_1'Aber was passiert, wenn wir dies sagen?
>>> if column("foo") == 5:
... print("yes")In früheren Versionen von SQLAlchemy war der zurückgegebene _BinaryExpression ein einfaches Python-Objekt, das zu True ausgewertet wurde. Jetzt wertet er aus, ob der tatsächliche ClauseElement denselben Hash-Wert haben sollte wie das verglichene Element. Das bedeutet:
>>> bool(column("foo") == 5)
False
>>> bool(column("foo") == column("foo"))
False
>>> c = column("foo")
>>> bool(c == c)
True
>>>Das bedeutet, dass Code wie der folgende
if expression:
print("the expression is:", expression)würde nicht ausgewertet werden, wenn expression eine binäre Klausel wäre. Da das obige Muster niemals verwendet werden sollte, löst die Basis-ClauseElement nun eine Ausnahme aus, wenn sie in einem booleschen Kontext aufgerufen wird.
>>> bool(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
...
raise TypeError("Boolean value of this clause is not defined")
TypeError: Boolean value of this clause is not definedCode, der die Anwesenheit eines ClauseElement-Ausdrucks überprüfen möchte, sollte stattdessen sagen:
if expression is not None:
print("the expression is:", expression)Denken Sie daran, **dies gilt auch für Tabellen- und Spaltenobjekte**.
Der Grund für die Änderung ist zweigeteilt:
Vergleiche der Form
if c1 == c2: <do something>können jetzt tatsächlich geschrieben werden.Unterstützung für korrektes Hashing von
ClauseElement-Objekten funktioniert nun auf alternativen Plattformen, nämlich Jython. Bis zu diesem Zeitpunkt verließ sich SQLAlchemy stark auf das spezifische Verhalten von cPython in dieser Hinsicht (und hatte damit immer noch gelegentliche Probleme).
Strengeres „executemany“-Verhalten¶
Ein „executemany“ in SQLAlchemy entspricht einem Aufruf von execute(), wobei eine Sammlung von Bindparameter-Sets übergeben wird.
connection.execute(table.insert(), {"data": "row1"}, {"data": "row2"}, {"data": "row3"})Wenn das Connection-Objekt die übergebene insert()-Konstruktion zur Kompilierung sendet, übergibt es dem Compiler die Schlüsselnamen aus dem ersten übergebenen Bindparameter-Set, um die Konstruktion der VALUES-Klausel der Anweisung zu bestimmen. Benutzer, die mit dieser Konstruktion vertraut sind, wissen, dass zusätzliche Schlüssel in den verbleibenden Wörterbüchern keine Auswirkung haben. Was jetzt anders ist, ist, dass alle nachfolgenden Wörterbücher mindestens *jeden* Schlüssel enthalten müssen, der im ersten Wörterbuch vorhanden ist. Das bedeutet, dass ein Aufruf wie dieser nicht mehr funktioniert:
connection.execute(
table.insert(),
{"timestamp": today, "data": "row1"},
{"timestamp": today, "data": "row2"},
{"data": "row3"},
)Da die dritte Zeile die Spalte ‚timestamp‘ nicht angibt. Frühere Versionen von SQLAlchemy fügten für diese fehlenden Spalten einfach NULL ein. Wenn die timestamp-Spalte im obigen Beispiel jedoch einen Python-seitigen Standardwert oder eine Funktion enthielt, würde diese *nicht* verwendet werden. Das liegt daran, dass die „executemany“-Operation für maximale Leistung über große Mengen von Parameter-Sets optimiert ist und keine Auswertung von Python-seitigen Standardwerten für fehlende Schlüssel versucht. Da Standardwerte oft entweder als SQL-Ausdrücke, die inline mit dem INSERT-Statement eingebettet sind, oder als serverseitige Ausdrücke implementiert sind, die wiederum basierend auf der Struktur des INSERT-Strings ausgelöst werden und per Definition nicht bedingt für jedes Parameter-Set ausgelöst werden können, wäre es inkonsistent, wenn Python-seitige Standardwerte anders als SQL/serverseitige Standardwerte funktionieren. (SQL-ausdrucksbasierte Standardwerte sind seit der 0.5er Serie inline eingebettet, wieder um die Auswirkungen großer Mengen von Parameter-Sets zu minimieren).
SQLAlchemy 0.6 stellt daher eine vorhersagbare Konsistenz her, indem es allen nachfolgenden Parameter-Sets verbietet, Felder leer zu lassen. Auf diese Weise gibt es keine stillschweigenden Fehler mehr bei Python-seitigen Standardwerten und Funktionen, die zusätzlich konsistent in ihrem Verhalten gegenüber SQL- und serverseitigen Standardwerten bleiben können.
UNION und andere „zusammengesetzte“ Konstrukte werden konsistent geklammert¶
Eine Regel, die zur Unterstützung von SQLite entwickelt wurde, wurde entfernt: Die erste zusammengesetzte Komponente innerhalb einer anderen zusammengesetzten Komponente (wie ein union() innerhalb eines except_()) würde nicht geklammert werden. Dies ist inkonsistent und liefert bei PostgreSQL, das Vorrangregeln für INTERSECTION hat, falsche Ergebnisse und ist im Allgemeinen eine Überraschung. Bei der Verwendung komplexer Komposite mit SQLite müssen Sie nun das erste Element in eine Subabfrage umwandeln (was auch bei PG kompatibel ist). Ein neues Beispiel findet sich im SQL-Ausdrucks-Tutorial am Ende von [https://sqlalchemy.de/docs/06/sqlexpression.html #unions-and-other-set-operations]. Mehr Hintergrundinformationen finden Sie unter #1665 und r6690.
C-Erweiterungen für das Abrufen von Ergebnissen¶
Der ResultProxy und verwandte Elemente, einschließlich der gebräuchlichsten „Zeilenverarbeitungs“-Funktionen wie Unicode-Konvertierung, numerische/boolesche Konvertierungen und Datumsparsing, wurden als optionale C-Erweiterungen zur Leistungssteigerung neu implementiert. Dies stellt den Anfang von SQLAlchemys Weg in die „dunkle Seite“ dar, auf der wir hoffen, die Leistung durch die Neuentwicklung kritischer Abschnitte in C weiter zu verbessern. Die Erweiterungen können durch Angabe von --with-cextensions erstellt werden, z.B. python setup.py --with- cextensions install.
Die Erweiterungen haben die dramatischste Auswirkung auf das Abrufen von Ergebnissen bei direktem Zugriff auf ResultProxy, d.h. auf das, was von engine.execute(), connection.execute() oder session.execute() zurückgegeben wird. Innerhalb der von einem ORM Query-Objekt zurückgegebenen Ergebnisse ist das Abrufen von Ergebnissen kein so hoher Anteil des Overheads, daher verbessern sich die ORM-Leistungen bescheidener, hauptsächlich im Bereich des Abrufens großer Ergebnismengen. Die Leistungsverbesserungen hängen stark vom verwendeten DBAPI und der Syntax zum Zugriff auf die Spalten jeder Zeile ab (z.B. ist row['name'] viel schneller als row.name). Die aktuellen Erweiterungen haben keine Auswirkung auf die Geschwindigkeit von Inserts/Updates/Deletes und verbessern auch nicht die Latenz der SQL-Ausführung, d.h. eine Anwendung, die die meiste Zeit mit der Ausführung vieler Anweisungen mit sehr kleinen Ergebnismengen verbringt, wird keine großen Verbesserungen feststellen.
Die Leistung wurde in 0.6 im Vergleich zu 0.5 unabhängig von den Erweiterungen verbessert. Ein kurzer Überblick darüber, wie das Verbinden und Abrufen von 50.000 Zeilen mit SQLite aussieht, bei weitgehend direktem SQLite-Zugriff, einem ResultProxy und einem einfachen, gemappten ORM-Objekt.
sqlite select/native: 0.260s
0.6 / C extension
sqlalchemy.sql select: 0.360s
sqlalchemy.orm fetch: 2.500s
0.6 / Pure Python
sqlalchemy.sql select: 0.600s
sqlalchemy.orm fetch: 3.000s
0.5 / Pure Python
sqlalchemy.sql select: 0.790s
sqlalchemy.orm fetch: 4.030sOben ruft das ORM die Zeilen 33% schneller ab als 0.5 aufgrund von Performance-Verbesserungen in Python. Mit den C-Erweiterungen erhalten wir weitere 20%. ResultProxy-Abrufe verbessern sich jedoch mit der C-Erweiterung um 67% gegenüber keiner Erweiterung. Andere Tests berichten von bis zu 200% Geschwindigkeitsverbesserung für einige Szenarien, z.B. dort, wo viele String-Konvertierungen stattfinden.
Neue Schemafunktionen¶
Das Paket sqlalchemy.schema hat einige lang erwartete Aufmerksamkeit erhalten. Die sichtbarste Änderung ist das neu erweiterte DDL-System. In SQLAlchemy war es seit Version 0.5 möglich, benutzerdefinierte DDL-Strings zu erstellen und diese mit Tabellen oder Metadatenobjekten zu verknüpfen.
from sqlalchemy.schema import DDL
DDL("CREATE TRIGGER users_trigger ...").execute_at("after-create", metadata)Jetzt ist die vollständige Suite von DDL-Konstrukten unter demselben System verfügbar, einschließlich derer für CREATE TABLE, ADD CONSTRAINT usw.
from sqlalchemy.schema import Constraint, AddConstraint
AddContraint(CheckConstraint("value > 5")).execute_at("after-create", mytable)Zusätzlich sind alle DDL-Objekte nun reguläre ClauseElement-Objekte, genau wie jedes andere SQLAlchemy-Ausdrucksobjekt.
from sqlalchemy.schema import CreateTable
create = CreateTable(mytable)
# dumps the CREATE TABLE as a string
print(create)
# executes the CREATE TABLE statement
engine.execute(create)und mit der Erweiterung sqlalchemy.ext.compiler können Sie Ihre eigenen erstellen.
from sqlalchemy.schema import DDLElement
from sqlalchemy.ext.compiler import compiles
class AlterColumn(DDLElement):
def __init__(self, column, cmd):
self.column = column
self.cmd = cmd
@compiles(AlterColumn)
def visit_alter_column(element, compiler, **kw):
return "ALTER TABLE %s ALTER COLUMN %s %s ..." % (
element.column.table.name,
element.column.name,
element.cmd,
)
engine.execute(AlterColumn(table.c.mycolumn, "SET DEFAULT 'test'"))Veraltete/Entfernte Schemaelemente¶
Das Schema-Paket wurde ebenfalls stark vereinfacht. Viele Optionen und Methoden, die während 0.5 als veraltet markiert waren, wurden entfernt. Andere wenig bekannte Accessoren und Methoden wurden ebenfalls entfernt.
Das Schlüsselwortargument „owner“ wurde aus
Tableentfernt. Verwenden Sie „schema“, um Namespaces darzustellen, die dem Tabellennamen vorangestellt werden.die veralteten
MetaData.connect()undThreadLocalMetaData.connect()wurden entfernt - senden Sie das Attribut „bind“, um ein Metadaten zu binden.veraltete metadata.table_iterator()-Methode entfernt (verwenden Sie sorted_tables).
Das Argument „metadata“ wurde aus
DefaultGeneratorund seinen Unterklassen entfernt, bleibt aber lokal aufSequencevorhanden, das eine eigenständige Konstruktion in DDL ist.veraltete
PassiveDefault- verwenden SieDefaultClause.Öffentliche Veränderbarkeit von
IndexundConstraint-Objekten entfernt.ForeignKeyConstraint.append_element()Index.append_column()UniqueConstraint.append_column()PrimaryKeyConstraint.add()PrimaryKeyConstraint.remove()
Diese sollten deklarativ (d.h. in einer Konstruktion) erstellt werden.
Weitere entfernte Dinge
Table.key(keine Ahnung, wofür das war)Column.bind(erhalten über column.table.bind)Column.metadata(erhalten über column.table.metadata)Column.sequence(verwenden Sie column.default)
Weitere Verhaltensänderungen¶
UniqueConstraint,Index,PrimaryKeyConstraintakzeptieren Listen von Spaltennamen oder Spaltenobjekten als Argumente.Das Flag
use_alterbeiForeignKeyist nun eine Abkürzungsoption für Operationen, die mit demDDL()-Ereignissystem von Hand konstruiert werden können. Eine Nebenwirkung dieses Refaktors ist, dassForeignKeyConstraint-Objekte mituse_alter=Truenicht auf SQLite ausgegeben werden, das ALTER für Fremdschlüssel nicht unterstützt. Dies hat keine Auswirkungen auf das Verhalten von SQLite, da SQLite FOREIGN KEY-Beschränkungen nicht tatsächlich beachtet.Table.primary_keyist nicht zuweisbar - verwenden Sietable.append_constraint(PrimaryKeyConstraint(...)).Eine
Column-Definition mit einemForeignKeyund ohne Typ, z.B.Column(name, ForeignKey(sometable.c.somecol)), erhielt früher den Typ der referenzierten Spalte. Jetzt ist die Unterstützung für diese automatische Typinferenz partiell und funktioniert möglicherweise nicht in allen Fällen.
Logging wurde geöffnet¶
Auf Kosten einiger zusätzlicher Methodenaufrufe können Sie nach der Erstellung einer Engine, eines Pools oder eines Mappers Protokollebenen für INFO und DEBUG festlegen, und das Logging wird gestartet. Die Methode isEnabledFor(INFO) wird nun pro Connection und isEnabledFor(DEBUG) pro ResultProxy aufgerufen, wenn sie bereits auf der übergeordneten Verbindung aktiviert ist. Pool-Logging sendet an log.info() und log.debug() ohne Prüfung – beachten Sie, dass das Auschecken/Einchecken von Pools typischerweise einmal pro Transaktion erfolgt.
Reflection/Inspector API¶
Das Reflection-System, das die Reflection von Tabellenspalten über Table('sometable', metadata, autoload=True) ermöglicht, wurde in eine eigene feingranulare API geöffnet, die eine direkte Inspektion von Datenbankelementen wie Tabellen, Spalten, Constraints, Indizes und mehr ermöglicht. Diese API gibt Rückgabewerte als einfache Listen von Strings, Wörterbüchern und TypeEngine-Objekten aus. Die Interna von autoload=True bauen nun auf diesem System auf, so dass die Übersetzung von rohen Datenbankinformationen in sqlalchemy.schema-Konstrukte zentralisiert ist und der Vertrag einzelner Dialekte stark vereinfacht wird, was Fehler und Inkonsistenzen zwischen verschiedenen Backends erheblich reduziert.
Um einen Inspektor zu verwenden.
from sqlalchemy.engine.reflection import Inspector
insp = Inspector.from_engine(my_engine)
print(insp.get_schema_names())Die Methode from_engine() bietet in einigen Fällen einen Backend-spezifischen Inspektor mit zusätzlichen Fähigkeiten, wie z.B. bei PostgreSQL, der eine Methode get_table_oid() bereitstellt.
my_engine = create_engine("postgresql://...")
pg_insp = Inspector.from_engine(my_engine)
print(pg_insp.get_table_oid("my_table"))RETURNING-Unterstützung¶
Die Konstrukte insert(), update() und delete() unterstützen nun eine Methode returning(), die der SQL-RETURNING-Klausel entspricht, wie sie von PostgreSQL, Oracle, MS-SQL und Firebird unterstützt wird. Für kein anderes Backend wird sie zu diesem Zeitpunkt unterstützt.
Gegeben eine Liste von Spaltenausdrücken auf die gleiche Weise wie bei einem select()-Konstrukt, werden die Werte dieser Spalten als reguläres Ergebnis-Set zurückgegeben.
result = connection.execute(
table.insert().values(data="some data").returning(table.c.id, table.c.timestamp)
)
row = result.first()
print("ID:", row["id"], "Timestamp:", row["timestamp"])Die Implementierung von RETURNING über die vier unterstützten Backends variiert stark, im Fall von Oracle erfordert sie eine komplizierte Nutzung von OUT-Parametern, die in ein „Mock“-Ergebnis-Set umgeleitet werden, und im Fall von MS-SQL wird eine umständliche SQL-Syntax verwendet. Die Verwendung von RETURNING unterliegt Einschränkungen.
sie funktioniert nicht für die Ausführung im „executemany()“-Stil. Dies ist eine Einschränkung aller unterstützten DBAPIs.
Einige Backends, wie Oracle, unterstützen nur RETURNING, das eine einzelne Zeile zurückgibt – dies schließt UPDATE- und DELETE-Anweisungen ein, was bedeutet, dass das update()- oder delete()-Konstrukt nur eine einzelne Zeile abgleichen muss, andernfalls wird ein Fehler ausgelöst (von Oracle, nicht von SQLAlchemy).
RETURNING wird auch automatisch von SQLAlchemy verwendet, wenn verfügbar und wenn nicht anders durch einen expliziten returning()-Aufruf angegeben, um den Wert neu generierter Primärschlüsselwerte für einzelne Zeilen-INSERT-Anweisungen abzurufen. Das bedeutet, dass es keine „SELECT nextval(sequence)“-Vorab-Ausführung mehr für INSERT-Anweisungen gibt, bei denen der Primärschlüsselwert benötigt wird. Ehrlich gesagt verursacht die implizite RETURNING-Funktion mehr Methoden-Overhead als das alte „select nextval()“-System, das einen schnellen und einfachen cursor.execute() zur Abfrage des Sequenzwerts verwendete und im Fall von Oracle zusätzliche Bindung von Out-Parametern erfordert. Wenn also der Methoden-/Protokoll-Overhead teurer ist als zusätzliche Datenbank-Round-Trips, kann die Funktion deaktiviert werden, indem implicit_returning=False an create_engine() übergeben wird.
Änderungen am Typsystem¶
Neue Architektur¶
Das Typsystem wurde hinter den Kulissen komplett überarbeitet, um zwei Ziele zu erreichen:
Trennung der Handhabung von Bindparametern und Ergebniszeilenwerten, typischerweise eine DBAPI-Anforderung, von der SQL-Spezifikation des Typs selbst, die eine Datenbankanforderung ist. Dies steht im Einklang mit der allgemeinen Dialekt-Refaktorierung, die das Datenbank-SQL-Verhalten vom DBAPI trennt.
Festlegung eines klaren und konsistenten Vertrags für die Generierung von DDL aus einem
TypeEngine-Objekt und für die Konstruktion vonTypeEngine-Objekten basierend auf Spalten-Reflection.
Höhepunkte dieser Änderungen sind:
Die Konstruktion von Typen innerhalb von Dialekten wurde komplett überarbeitet. Dialekte definieren nun öffentlich verfügbare Typen ausschließlich als GROSSBUCHSTABEN-Namen und interne Implementierungstypen mit Unterstrich-Bezeichnern (d.h. privat). Das System, mit dem Typen in SQL und DDL ausgedrückt werden, wurde in das Compiler-System verschoben. Dies hat zur Folge, dass es in den meisten Dialekten viel weniger Typobjekte gibt. Ein detailliertes Dokument zu dieser Architektur für Dialekt-Autoren finden Sie unter [source:/lib/sqlalc hemy/dialects/type_migration_guidelines.txt].
Die Reflection von Typen gibt nun den genauen GROSSBUCHSTABEN-Typ in types.py zurück, oder den GROSSBUCHSTABEN-Typ im Dialekt selbst, wenn der Typ kein Standard-SQL-Typ ist. Das bedeutet, dass Reflection nun genauere Informationen über reflektierte Typen liefert.
Benutzerdefinierte Typen, die
TypeEngineuntervererben undget_col_spec()bereitstellen möchten, sollten nunUserDefinedTypeuntervererben.Die Methode
result_processor()in allen Typklassen akzeptiert nun ein zusätzliches Argumentcoltype. Dies ist der DBAPI-Typobjekt, das an cursor.description angehängt ist, und sollte bei Bedarf verwendet werden, um bessere Entscheidungen darüber zu treffen, welche Art von Ergebnisverarbeitungsaufrufer zurückgegeben werden soll. Idealerweise müssten Ergebnisverarbeitungsfunktionen nieisinstance()verwenden, was auf dieser Ebene ein teurer Aufruf ist.
Nativer Unicode-Modus¶
Da mehr DBAPIs das direkte Zurückgeben von Python-Unicode-Objekten unterstützen, führt der Basis-Dialekt bei der ersten Verbindung eine Prüfung durch, ob der DBAPI bei einer einfachen Auswahl eines VARCHAR-Wertes ein Python-Unicode-Objekt zurückgibt. Wenn ja, überspringt der Typ String und alle Unterklassen (d.h. Text, Unicode usw.) den Schritt der „Unicode“-Prüfung/Konvertierung beim Empfang von Ergebniszeilen. Dies bietet eine dramatische Leistungssteigerung für große Ergebnismengen. Der „Unicode-Modus“ funktioniert derzeit bekanntermaßen mit
sqlite3 / pysqlite
psycopg2 - SQLA 0.6 verwendet nun standardmäßig den „UNICODE“-Typ-Erweiterung für jedes psycopg2-Verbindungsobjekt.
pg8000
cx_oracle (wir verwenden einen Ausgabeprozessor – nette Funktion!)
Andere Typen können die Unicode-Verarbeitung bei Bedarf deaktivieren, wie z.B. der Typ NVARCHAR bei Verwendung mit MS-SQL.
Insbesondere bei der Portierung einer Anwendung, die auf einem DBAPI basiert, der früher Nicht-Unicode-Strings zurückgab, verhält sich der „native Unicode“-Modus standardmäßig deutlich anders – Spalten, die als String oder VARCHAR deklariert sind, geben jetzt standardmäßig Unicode zurück, während sie vorher Strings zurückgaben. Dies kann Code brechen, der Nicht-Unicode-Strings erwartet. Der „native Unicode“-Modus von psycopg2 kann durch Übergabe von use_native_unicode=False an create_engine() deaktiviert werden.
Eine allgemeinere Lösung für Zeichenspalten, die explizit kein Unicode-Objekt wünschen, ist die Verwendung eines TypeDecorator, der Unicode zurück in utf-8 oder was auch immer gewünscht ist, konvertiert.
class UTF8Encoded(TypeDecorator):
"""Unicode type which coerces to utf-8."""
impl = sa.VARCHAR
def process_result_value(self, value, dialect):
if isinstance(value, unicode):
value = value.encode("utf-8")
return valueBeachten Sie, dass das Flag assert_unicode nun veraltet ist. SQLAlchemy erlaubt dem DBAPI und der verwendeten Backend-Datenbank, Unicode-Parameter zu handhaben, wenn verfügbar, und fügt keinen operativen Overhead durch die Prüfung des eingehenden Typs hinzu; moderne Systeme wie SQLite und PostgreSQL lösen eine Kodierungsfehlermeldung auf ihrer Seite aus, wenn ungültige Daten übergeben werden. In Fällen, in denen SQLAlchemy einen Bindparameter von Python-Unicode in einen kodierten String umwandeln muss oder wenn der Unicode-Typ explizit verwendet wird, wird eine Warnung ausgegeben, wenn das Objekt eine Bytestring ist. Diese Warnung kann mit dem Python-Warnungsfilter unter https://docs.pythonlang.de/library/warnings.html unterdrückt oder in eine Ausnahme umgewandelt werden.
Generischer Enum-Typ¶
Wir haben nun ein Enum im Modul types. Dies ist ein String-Typ, dem eine Sammlung von „Labels“ zugewiesen wird, die die möglichen Werte für diese Labels einschränken. Standardmäßig erzeugt dieser Typ ein VARCHAR mit der Größe des größten Labels und wendet eine CHECK-Beschränkung auf die Tabelle innerhalb der CREATE TABLE-Anweisung an. Bei Verwendung von MySQL verwendet der Typ standardmäßig den ENUM-Typ von MySQL, und bei Verwendung von PostgreSQL erzeugt der Typ einen benutzerdefinierten Typ mit CREATE TYPE <mytype> AS ENUM. Um den Typ unter PostgreSQL zu erstellen, muss der Parameter name dem Konstruktor übergeben werden. Der Typ akzeptiert auch die Option native_enum=False, die für alle Datenbanken die VARCHAR/CHECK-Strategie ausgibt. Beachten Sie, dass PostgreSQL ENUM-Typen derzeit nicht mit pg8000 oder zxjdbc funktionieren.
Reflection gibt Dialekt-spezifische Typen zurück¶
Reflection gibt nun den spezifischsten Typ zurück, der aus der Datenbank möglich ist. Das heißt, wenn Sie eine Tabelle mit String erstellen und diese dann zurückspiegeln, ist die gespiegelte Spalte wahrscheinlich VARCHAR. Für Dialekte, die eine spezifischere Form des Typs unterstützen, erhalten Sie diese. So wäre ein Text-Typ unter Oracle ein oracle.CLOB, ein LargeBinary könnte ein mysql.MEDIUMBLOB sein usw. Der offensichtliche Vorteil hierbei ist, dass Reflection so viele Informationen wie möglich von dem bewahrt, was die Datenbank zu sagen hatte.
Einige Anwendungen, die sich stark mit Tabellenmetadaten beschäftigen, möchten möglicherweise Typen über gespiegelte Tabellen und/oder nicht gespiegelte Tabellen hinweg vergleichen. Es gibt einen semi-privaten Zugriff auf TypeEngine namens _type_affinity und eine zugehörige Vergleichshilfe _compare_type_affinity. Dieser Zugriff gibt die „generische“ types-Klasse zurück, der der Typ entspricht.
>>> String(50)._compare_type_affinity(postgresql.VARCHAR(50))
True
>>> Integer()._compare_type_affinity(mysql.REAL)
FalseSonstige API-Änderungen¶
Die üblichen „generischen“ Typen sind weiterhin das allgemeine System, d.h. String, Float, DateTime. Hier gibt es einige Änderungen.
Typen raten nicht mehr nach Standardparametern. Insbesondere
Numeric,Floatsowie die Unterklassen NUMERIC, FLOAT, DECIMAL erzeugen keine Länge oder Skalierung, es sei denn, sie werden angegeben. Dies schließt weiterhin die umstrittenen TypenStringundVARCHARein (obwohl der MySQL-Dialekt präventiv einen Fehler auslöst, wenn er aufgefordert wird, VARCHAR ohne Länge zu rendern). Es werden keine Standardwerte angenommen, und wenn sie in einer CREATE TABLE-Anweisung verwendet werden, wird ein Fehler ausgelöst, wenn die zugrunde liegende Datenbank keine Versionen dieser Typen ohne Längenangabe zulässt.Der Typ
Binarywurde inLargeBinaryumbenannt, für BLOB/BYTEA/ähnliche Typen. FürBINARYundVARBINARYsind diese direkt alstypes.BINARY,types.VARBINARYsowie in den MySQL- und MS-SQL-Dialekten vorhanden.PickleTypeverwendet nun == für den Vergleich von Werten, wenn mutable=True ist, es sei denn, das Argument „comparator“ mit einer Vergleichsfunktion wird dem Typ übergeben. Wenn Sie ein benutzerdefiniertes Objekt pickeln, sollten Sie eine Methode__eq__()implementieren, damit wertbasierte Vergleiche korrekt sind.Die Standardargumente „precision“ und „scale“ von Numeric und Float wurden entfernt und haben nun den Standardwert None. NUMERIC und FLOAT werden standardmäßig ohne numerische Argumente gerendert, es sei denn, diese Werte werden bereitgestellt.
DATE, TIME und DATETIME Typen auf SQLite können nun optionale Argumente „storage_format“ und „regexp“ annehmen. „storage_format“ kann verwendet werden, um diese Typen mit einem benutzerdefinierten String-Format zu speichern. „regexp“ erlaubt die Verwendung eines benutzerdefinierten regulären Ausdrucks, um String-Werte aus der Datenbank abzugleichen.
Die Unterstützung für
__legacy_microseconds__bei denTimeundDateTimeTypen von SQLite ist nicht mehr gegeben. Verwenden Sie stattdessen das neue Argument „storage_format“.Die
DateTime-Typen unter SQLite verwenden nun standardmäßig einen strengeren regulären Ausdruck, um Strings aus der Datenbank abzugleichen. Verwenden Sie das neue Argument „regexp“, wenn Sie Daten in einem Legacy-Format verwenden.
ORM-Änderungen¶
Das Upgrade einer ORM-Anwendung von 0.5 auf 0.6 sollte nur geringe oder gar keine Änderungen erfordern, da das Verhalten des ORM praktisch identisch bleibt. Es gibt einige Änderungen bei Standardargumenten und Namen, und einige Ladeverhalten wurden verbessert.
Neue Unit of Work¶
Die interne Funktionsweise der Unit of Work, hauptsächlich topological.py und unitofwork.py, wurde komplett neu geschrieben und ist stark vereinfacht. Dies sollte keine Auswirkungen auf die Nutzung haben, da alle vorhandenen Verhaltensweisen während des Flushes exakt beibehalten wurden (oder zumindest so weit, wie sie von unserer Testsuite und den wenigen Produktionsumgebungen, die sie intensiv getestet haben, abgedeckt werden). Die Leistung von flush() verwendet nun 20-30% weniger Methodenaufrufe und sollte auch weniger Speicher verbrauchen. Die Absicht und der Fluss des Quellcodes sollten nun einigermaßen leicht nachvollziehbar sein, und die Architektur des Flush ist zu diesem Zeitpunkt ziemlich offen, was Raum für potenzielle neue Bereiche der Raffinesse schafft. Der Flush-Prozess ist nicht mehr rekursiv, so dass Flush-Pläne beliebiger Größe und Komplexität geflusht werden können. Darüber hinaus zwischenspeichert der „save“-Prozess des Mappers, der INSERT- und UPDATE-Anweisungen ausgibt, nun die „kompilierte“ Form der beiden Anweisungen, so dass die Aufrufzahlen bei sehr großen Flushes dramatisch reduziert werden.
Jegliche Änderungen im Verhalten des Flushes im Vergleich zu früheren Versionen von 0.6 oder 0.5 sollten uns umgehend gemeldet werden – wir werden sicherstellen, dass keine Funktionalität verloren geht.
Änderungen an query.update() und query.delete()¶
Die Option „expire“ bei query.update() wurde in „fetch“ umbenannt, um mit der von query.delete() übereinzustimmen.
query.update()undquery.delete()verwenden standardmäßig „evaluate“ für die Synchronisierungsstrategie.Die „synchronize“-Strategie für update() und delete() löst bei einem Fehler eine Ausnahme aus. Es gibt kein implizites Fallback auf „fetch“. Das Fehlschlagen der Auswertung basiert auf der Struktur der Kriterien, so dass Erfolg/Fehler deterministisch anhand der Code-Struktur ist.
relation() heißt nun offiziell relationship()¶
Dies soll das langwierige Problem lösen, dass „relation“ im Sinne der relationalen Algebra eine „Tabelle oder abgeleitete Tabelle“ bedeutet. Der Name relation(), der weniger Tipparbeit erfordert, wird auf absehbare Zeit beibehalten, sodass diese Änderung völlig schmerzlos sein sollte.
Subquery Eager Loading¶
Eine neue Art des Eager Loadings wurde hinzugefügt, genannt „subquery“ Loading. Dies ist ein Load, das unmittelbar nach dem ersten eine zweite SQL-Abfrage ausgibt, die vollständige Sammlungen für alle Eltern des ersten Queries lädt und über INNER JOIN nach oben zum Elternteil verbindet. Subquery Loading wird ähnlich wie das aktuelle joined-eager Loading verwendet, mit den Optionen `subqueryload()`` und ``subqueryload_all()`` sowie der Einstellung ``lazy='subquery'`` bei ``relationship()`. Das Subquery-Load ist in der Regel deutlich effizienter für das Laden vieler größerer Sammlungen, da es bedingungslos INNER JOIN verwendet und auch keine Elternzeilen erneut lädt.
`eagerload()``, ``eagerload_all()`` ist nun ``joinedload()``, ``joinedload_all()`¶
Um Platz für die neue Subquery-Load-Funktion zu schaffen, werden die bestehenden Optionen `eagerload()``/``eagerload_all()`` nun von ``joinedload()`` und ``joinedload_all()`` abgelöst. Die alten Namen bleiben wie ``relation()` auf absehbare Zeit erhalten.
`lazy=False|None|True|'dynamic'`` akzeptiert nun ``lazy='noload'|'joined'|'subquery'|'select'|'dynamic'`¶
Weiter im Thema der erweiterten Laderstrategien sind die Standard-Schlüsselwörter für die `lazy``-Option bei ``relationship()`` nun ``select`` für Lazy Loading (über ein SELECT bei Attributzugriff), ``joined`` für Joined-Eager-Loading, ``subquery`` für Subquery-Eager-Loading, ``noload`` für kein Laden und ``dynamic`` für eine „dynamische“ Beziehung. Die alten Argumente ``True``, ``False``, ``None` werden weiterhin mit identischem Verhalten wie zuvor akzeptiert.
innerjoin=True bei relation, joinedload¶
Skalare und Sammlungen, die per Joined-Eager-Loading geladen werden, können nun angewiesen werden, INNER JOIN anstelle von OUTER JOIN zu verwenden. Unter PostgreSQL wird dies bei einigen Abfragen zu einer Geschwindigkeitssteigerung von 300-600 % führen. Setzen Sie dieses Flag für alle Many-to-One-Beziehungen, die auf einem NICHT-NULLABLE-Fremdschlüssel basieren, und ähnlich für alle Sammlungen, bei denen verwandte Elemente garantiert existieren.
Auf Mapper-Ebene
mapper(Child, child)
mapper(
Parent,
parent,
properties={"child": relationship(Child, lazy="joined", innerjoin=True)},
)Auf Abfrage-Ebene
session.query(Parent).options(joinedload(Parent.child, innerjoin=True)).all()Das Flag innerjoin=True auf der Ebene der relationship()-Definition wird auch für jede joinedload()-Option wirksam, die den Wert nicht überschreibt.
Verbesserungen bei Many-to-One¶
Many-to-One-Beziehungen lösen nun seltener ein Lazyload aus, einschließlich der Fälle, in denen der „alte“ Wert bei Ersetzung eines neuen nicht mehr abgerufen wird.
Eine Many-to-One-Beziehung zu einer Joined-Table-Subklasse verwendet nun get() für ein einfaches Laden (bekannt als „use_get“-Bedingung), d.h.
Related->``Sub(Base)``, ohne dass die Primaryjoin-Bedingung im Hinblick auf die Basistabelle neu definiert werden muss. [ticket:1186]Die Angabe eines Fremdschlüssels mit einer deklarativen Spalte, d.h.
ForeignKey(MyRelatedClass.id), verhindert nicht, dass die „use_get“-Bedingung greift [ticket:1492].relationship(), joinedload() und joinedload_all() verfügen nun über eine Option namens „innerjoin“. Geben Sie
TrueoderFalsean, um zu steuern, ob ein Eager-Join als INNER oder OUTER Join konstruiert wird. Standard ist wie immerFalse. Die Mapper-Optionen überschreiben jede auf relationship() gesetzte Einstellung. Sollte generell für Many-to-One-Beziehungen mit nicht-nullable Fremdschlüsseln gesetzt werden, um die Join-Leistung zu verbessern. [ticket:1544]Das Verhalten des Joined Eager Loadings, bei dem die Hauptabfrage bei vorhandenem LIMIT/OFFSET in eine Subquery eingeschlossen wird, macht nun eine Ausnahme, wenn alle Eager Loads Many-to-One-Joins sind. In diesen Fällen erfolgen die Eager-Joins direkt zur Eltern-Tabelle zusammen mit LIMIT/OFFSET, ohne den zusätzlichen Overhead einer Subquery, da ein Many-to-One-Join keine Zeilen zum Ergebnis hinzufügt.
Zum Beispiel würde diese Abfrage in 0.5
session.query(Address).options(eagerload(Address.user)).limit(10)
SQL wie folgt erzeugen:
SELECT * FROM (SELECT * FROM addresses LIMIT 10) AS anon_1 LEFT OUTER JOIN users AS users_1 ON users_1.id = anon_1.addresses_user_id
Dies liegt daran, dass die Anwesenheit von Eager Loadern darauf hindeutet, dass einige oder alle davon sich auf Mehrzeilen-Sammlungen beziehen könnten, was jede Art von zeilenanzahl-sensitiven Modifikatoren wie LIMIT in eine Subquery einschließen würde.
In 0.6 ist diese Logik empfindlicher und kann erkennen, ob alle Eager Loader Many-to-Ones darstellen, in diesem Fall beeinflussen die Eager-Joins die Zeilenanzahl nicht.
SELECT * FROM addresses LEFT OUTER JOIN users AS users_1 ON users_1.id = addresses.user_id LIMIT 10
Mutable Primary Keys mit Joined Table Inheritance¶
Eine Joined-Table-Inheritance-Konfiguration, bei der die Kindtabelle einen PK hat, der auf den Eltern-PK verweist, kann nun auf einer CASCADE-fähigen Datenbank wie PostgreSQL aktualisiert werden. mapper() hat nun eine Option passive_updates=True, die angibt, dass dieser Fremdschlüssel automatisch aktualisiert wird. Wenn auf einer nicht-kaskadierenden Datenbank wie SQLite oder MySQL/MyISAM, setzen Sie dieses Flag auf False. Eine zukünftige Funktionserweiterung wird versuchen, dieses Flag automatisch basierend auf dem verwendeten Dialekt/Tabellenstil zu konfigurieren.
Beaker Caching¶
Ein vielversprechendes neues Beispiel für die Beaker-Integration befindet sich in examples/beaker_caching. Dies ist ein unkompliziertes Rezept, das einen Beaker-Cache innerhalb der Ergebnisgenerierungs-Engine von Query anwendet. Cache-Parameter werden über query.options() bereitgestellt und ermöglichen die vollständige Kontrolle über den Inhalt des Caches. SQLAlchemy 0.6 enthält Verbesserungen an der Methode Session.merge(), um diese und ähnliche Rezepte zu unterstützen, sowie um in den meisten Szenarien eine deutlich verbesserte Leistung zu erzielen.
Sonstige Änderungen¶
Das „row tuple“-Objekt, das von
Queryzurückgegeben wird, wenn mehrere Spalten/Entitäten ausgewählt werden, ist nun pickelbar und performanter.query.join()wurde überarbeitet, um konsistenteres Verhalten und mehr Flexibilität zu bieten (enthält [ticket:1537]).query.select_from()akzeptiert mehrere Klauseln, um mehrere durch Kommas getrennte Einträge in der FROM-Klausel zu erzeugen. Nützlich beim Auswählen aus mehrfach verbundenen join()-Klauseln.Das Flag „dont_load=True“ bei
Session.merge()ist veraltet und heißt nun „load=False“.Die Hilfsfunktion „make_transient()“ wurde hinzugefügt, die eine persistente/getrennte Instanz in eine transiente Instanz umwandelt (d.h. die instance_key löscht und aus jeder Session entfernt) [ticket:1052].
Das Flag allow_null_pks bei mapper() ist veraltet und wurde in allow_partial_pks umbenannt. Es ist standardmäßig „aktiviert“. Das bedeutet, dass eine Zeile, die einen Nicht-NULL-Wert für eine ihrer Primärschlüsselspalten aufweist, als Identität betrachtet wird. Die Notwendigkeit dieses Szenarios tritt typischerweise nur beim Mapping auf einen Outer Join auf. Wenn auf False gesetzt, wird ein PK, der NULL-Werte enthält, nicht als Primärschlüssel betrachtet – dies bedeutet insbesondere, dass eine Ergebniszeile als None zurückkommt (oder nicht in eine Sammlung eingefügt wird), und neu in 0.6 bedeutet dies auch, dass session.merge() für einen solchen PK-Wert keinen Roundtrip zur Datenbank auslöst. [ticket:1680].
Die Mechanik von „backref“ wurde vollständig in das feingranularere „back_populates“-System integriert und findet vollständig innerhalb der Methode
_generate_backref()vonRelationPropertystatt. Dies vereinfacht das Initialisierungsverfahren vonRelationPropertyund ermöglicht eine einfachere Weitergabe von Einstellungen (z.B. von Unterklassen vonRelationProperty) an die umgekehrte Referenz. Die interneBackRef()ist weg undbackref()gibt ein einfaches Tupel zurück, das vonRelationPropertyverstanden wird.Das Attribut keys von
ResultProxyist nun eine Methode, daher müssen Referenzen darauf (result.keys) zu Methodenaufrufen geändert werden (result.keys()).ResultProxy.last_inserted_idsist nun veraltet, verwenden Sie stattdessenResultProxy.inserted_primary_key.
Veraltete/Entfernte ORM-Elemente¶
Die meisten Elemente, die während 0.5 als veraltet markiert waren und Deprecation-Warnungen auslösten, wurden entfernt (mit wenigen Ausnahmen). Alle Elemente, die als „pending deprecation“ markiert waren, sind nun veraltet und lösen bei Gebrauch eine Warnung aus.
Das Flag ‚transactional‘ bei sessionmaker() und anderen wurde entfernt. Verwenden Sie ‚autocommit=True‘, um ‚transactional=False‘ anzuzeigen.
Das Argument ‚polymorphic_fetch‘ bei mapper() wurde entfernt. Das Laden kann mit der Option ‚with_polymorphic‘ gesteuert werden.
Das Argument ‚select_table‘ bei mapper() wurde entfernt. Verwenden Sie ‚with_polymorphic=(„*“, <eine wählbare Auswahl>)‘ für diese Funktionalität.
Das Argument ‚proxy‘ bei synonym() wurde entfernt. Dieses Flag hatte während 0.5 keine Funktion, da das Verhalten der „Proxy-Generierung“ nun automatisch erfolgt.
Das Übergeben einer einzelnen Liste von Elementen an joinedload(), joinedload_all(), contains_eager(), lazyload(), defer() und undefer() anstelle von mehreren Positionsargumenten (*args) ist veraltet.
Das Übergeben einer einzelnen Liste von Elementen an query.order_by(), query.group_by(), query.join() oder query.outerjoin() anstelle von mehreren Positionsargumenten (*args) ist veraltet.
query.iterate_instances()wurde entfernt. Verwenden Siequery.instances().Query.query_from_parent()wurde entfernt. Verwenden Sie die Funktion sqlalchemy.orm.with_parent(), um eine „parent“-Klausel zu erzeugen, oder alternativquery.with_parent().query._from_self()wurde entfernt, verwenden Sie stattdessenquery.from_self().Das Argument „comparator“ für composite() wurde entfernt. Verwenden Sie stattdessen „comparator_factory“.
RelationProperty._get_join()wurde entfernt.Das Flag ‚echo_uow‘ bei Session wurde entfernt. Verwenden Sie Logging unter dem Namen „sqlalchemy.orm.unitofwork“.
session.clear()wurde entfernt. Verwenden Sie stattdessensession.expunge_all().session.save(),session.update(),session.save_or_update()wurden entfernt. Verwenden Sie stattdessensession.add()undsession.add_all().Das Flag „objects“ bei session.flush() bleibt veraltet.
Das Flag „dont_load=True“ bei session.merge() ist veraltet zugunsten von „load=False“.
ScopedSession.mapperbleibt veraltet. Sehen Sie das Usage Recipe unter https://sqlalchemy.de/trac/wiki/Usag eRecipes/SessionAwareMapperDas Übergeben einer
InstanceState(internes SQLAlchemy-Zustandsobjekt) anattributes.init_collection()oderattributes.get_history()ist veraltet. Diese Funktionen sind öffentliche APIs und erwarten normalerweise eine reguläre gemappte Objektinstanz.Der Parameter ‚engine‘ für
declarative_base()wurde entfernt. Verwenden Sie das Schlüsselwortargument ‚bind‘.
Erweiterungen¶
SQLSoup¶
SQLSoup wurde modernisiert und aktualisiert, um die üblichen Fähigkeiten von 0.5/0.6 zu berücksichtigen, einschließlich einer gut definierten Session-Integration. Lesen Sie bitte die neuen Dokumente unter [https://sqlalchemy.de/docs/06/reference/ext/sqlsoup.html].
Deklarativ¶
Die DeclarativeMeta (Standard-Metaklasse für declarative_base) erlaubte zuvor Unterklassen, dict_ zu modifizieren, um Klassenattribute (z.B. Spalten) hinzuzufügen. Dies funktioniert nicht mehr, der Konstruktor von DeclarativeMeta ignoriert nun dict_. Stattdessen sollten die Klassenattribute direkt zugewiesen werden, z.B. cls.id=Column(...), oder es sollte der Ansatz der MixIn-Klasse anstelle des Metaklassen-Ansatzes verwendet werden.
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