Was gibt es Neues in SQLAlchemy 0.7?

Über dieses Dokument

Dieses Dokument beschreibt die Änderungen zwischen SQLAlchemy Version 0.6, zuletzt veröffentlicht am 5. Mai 2012, und SQLAlchemy Version 0.7, die seit Oktober 2012 in Wartungs-Releases veröffentlicht wird.

Dokumentdatum: 27. Juli 2011

Einleitung

Dieser Leitfaden stellt die Neuerungen in SQLAlchemy Version 0.7 vor und dokumentiert auch Änderungen, die Benutzer beim Migrieren ihrer Anwendungen von der 0.6er-Serie von SQLAlchemy auf 0.7 betreffen.

Soweit es möglich ist, werden Änderungen so vorgenommen, dass sie die Kompatibilität mit für 0.6 erstellten Anwendungen nicht beeinträchtigen. Die Änderungen, die notwendigerweise nicht abwärtskompatibel sind, sind sehr wenige, und fast alle, abgesehen von der Änderung der Standardwerte für veränderliche Attribute, sollten eine äußerst kleine Anzahl von Anwendungen betreffen – viele der Änderungen beziehen sich auf nicht-öffentliche APIs und undokumentierte Hacks, die einige Benutzer möglicherweise versucht haben zu verwenden.

Eine zweite, noch kleinere Klasse von nicht abwärtskompatiblen Änderungen wird ebenfalls dokumentiert. Diese Klasse von Änderungen betrifft solche Funktionen und Verhaltensweisen, die seit Version 0.5 mindestens als veraltet gelten und seit ihrer Abschaffung Warnungen auslösen. Diese Änderungen würden nur Anwendungen betreffen, die noch APIs im Stil von 0.4 oder frühem 0.5 verwenden. Da das Projekt reift, haben wir immer weniger solcher Änderungen mit 0.x-Releases, was ein Produkt davon ist, dass die API immer weniger Funktionen hat, die für die Anwendungsfälle, für die sie gedacht waren, weniger als ideal sind.

Eine Reihe bestehender Funktionalitäten wurden in SQLAlchemy 0.7 abgelöst. Es gibt nicht viel Unterschied zwischen den Begriffen "abgelöst" und "veraltet", außer dass der erstere eine viel schwächere Andeutung hat, dass die alte Funktion jemals entfernt werden würde. In 0.7 wurden Funktionen wie synonym und comparable_property, sowie alle Extension- und andere Ereignisklassen, abgelöst. Aber diese "abgelösten" Funktionen wurden neu implementiert, so dass ihre Implementierungen größtenteils außerhalb des Kern-ORM-Codes liegen, so dass ihr fortgesetztes "Dabeibleiben" SQLAlchemy's Fähigkeit, seine Interna weiter zu optimieren und zu verfeinern, nicht beeinträchtigt, und wir erwarten, dass sie auf absehbare Zeit in der API bleiben.

Neue Funktionen

Neues Ereignissystem

SQLAlchemy begann früh mit der Klasse MapperExtension, die Hooks in den Persistenzzyklus von Mappern bot. Da SQLAlchemy schnell komponentisierter wurde und Mapper in eine stärker fokussierte Konfigurationsrolle drängte, tauchten viele weitere "Extension", "Listener" und "Proxy"-Klassen auf, um verschiedene Anwendungsfälle zur Aktivitätsunterbrechung ad hoc zu lösen. Ein Teil davon wurde durch die Divergenz der Aktivitäten angetrieben; ConnectionProxy-Objekte wollten ein System zum Umschreiben von Anweisungen und Parametern bereitstellen; AttributeExtension bot ein System zum Ersetzen eingehender Werte, und DDL-Objekte hatten Ereignisse, die von dialektsensitiven Callables umgeschaltet werden konnten.

0.7 implementiert praktisch alle diese Plugin-Punkte mit einem neuen, einheitlichen Ansatz neu, der alle Funktionalitäten der verschiedenen Systeme beibehält, mehr Flexibilität und weniger Boilerplate bietet, besser leistet und die Notwendigkeit eliminiert, radikal unterschiedliche APIs für jedes Ereignissubsystem zu lernen. Die bereits vorhandenen Klassen MapperExtension, SessionExtension, AttributeExtension, ConnectionProxy, PoolListener sowie die Methode DDLElement.execute_at sind veraltet und werden jetzt basierend auf dem neuen System implementiert – diese APIs bleiben voll funktionsfähig und werden voraussichtlich auf absehbare Zeit bestehen bleiben.

Der neue Ansatz verwendet benannte Ereignisse und benutzerdefinierte Callables, um Aktivitäten mit Ereignissen zu verknüpfen. Das Aussehen und Verhalten der API wurde von so unterschiedlichen Quellen wie JQuery, Blinker und Hibernate beeinflusst und wurde auch bei mehreren Gelegenheiten während Konferenzen mit Dutzenden von Benutzern auf Twitter modifiziert, was für solche Fragen eine viel höhere Antwortrate zu haben scheint als die Mailingliste.

Es bietet auch ein offenes System zur Spezifizierung von Zielen, das es ermöglicht, Ereignisse mit API-Klassen zu verknüpfen, z.B. für alle Session- oder Engine-Objekte, mit spezifischen Instanzen von API-Klassen, z.B. für einen bestimmten Pool oder Mapper, sowie für verwandte Objekte wie eine benutzerdefinierte Klasse, die abgebildet wird, oder etwas so Spezifisches wie ein bestimmtes Attribut bei Instanzen einer bestimmten Unterklasse einer abgebildeten Elternklasse. Einzelne Listener-Subsysteme können Wrapper auf eingehende, vom Benutzer definierte Listener-Funktionen anwenden, die modifizieren, wie sie aufgerufen werden – ein Mapper-Ereignis kann entweder die Instanz des bearbeiteten Objekts oder sein zugrunde liegendes InstanceState-Objekt erhalten. Ein Attributereignis kann wählen, ob es die Verantwortung hat, einen neuen Wert zurückzugeben.

Mehrere Systeme bauen nun auf der neuen Event-API auf, darunter die neue "mutable attributes"-API sowie Composite-Attribute. Die stärkere Betonung von Ereignissen hat auch zur Einführung einer Handvoll neuer Ereignisse geführt, darunter Attributablauf und Aktualisierungsoperationen, Pickle-Lade-/Dump-Operationen, abgeschlossene Mapper-Konstruktionsoperationen.

Siehe auch

Events

#1902

Hybrid-Attribute, implementiert/ersetzt synonym(), comparable_property()

Das Beispiel "derived attributes" wurde nun in eine offizielle Erweiterung umgewandelt. Der typische Anwendungsfall für synonym() ist die Bereitstellung eines Deskriptor-Zugriffs auf eine abgebildete Spalte; der Anwendungsfall für comparable_property() ist die Möglichkeit, einen PropComparator von jedem Deskriptor zurückzugeben. In der Praxis ist der Ansatz des "Derived" einfacher zu verwenden, erweiterbarer, in wenigen Dutzend Zeilen reinem Python mit fast keinen Importen implementiert und erfordert nicht einmal, dass der ORM-Kern ihn kennt. Das Feature ist jetzt als "Hybrid Attributes" Erweiterung bekannt.

synonym() und comparable_property() sind weiterhin Teil des ORM, obwohl ihre Implementierungen nach außen verlagert wurden und auf einem Ansatz basieren, der dem der Hybrid-Erweiterung ähnelt, so dass die Kernmodule Mapper/Query/Property ansonsten nicht davon wissen.

Siehe auch

Hybrid-Attribute

#1903

Geschwindigkeitsverbesserungen

Wie üblich bei allen großen SQLA-Releases wurde eine umfassende Überarbeitung der Interna vorgenommen, um Overhead und Aufrufe zu reduzieren, was die Arbeit in häufigen Szenarien weiter reduziert. Highlights dieser Version sind:

  • Der Flush-Prozess bündelt nun INSERT-Anweisungen in Batches, die an cursor.executemany() übergeben werden, für Zeilen, bei denen der Primärschlüssel bereits vorhanden ist. Dies gilt insbesondere für die "Child"-Tabelle in einer Joined Table Inheritance-Konfiguration, was bedeutet, dass die Anzahl der Aufrufe an cursor.execute für eine große Masseneinfügung von Joined Table-Objekten halbiert werden kann, wodurch native DBAPI-Optimierungen für die an cursor.executemany() übergebenen Anweisungen (wie die Wiederverwendung einer vorbereiteten Anweisung) ermöglicht werden.

  • Der Code-Pfad, der beim Zugriff auf eine Many-to-One-Referenz auf ein bereits geladenes zugehöriges Objekt aufgerufen wird, wurde stark vereinfacht. Die Identitätszuordnung wird direkt überprüft, ohne dass zuerst ein neues Query-Objekt generiert werden muss, was im Kontext von Tausenden von Many-to-Ones im Speicher teuer ist. Die Verwendung von pro Aufruf konstruierten "Loader"-Objekten wird auch für die Mehrheit der lazy Attributladungen nicht mehr verwendet.

  • Die Neufassung von Kompositen ermöglicht einen kürzeren Code-Pfad, wenn Mapper-Interna abgebildete Attribute während eines Flushs zugreifen.

  • Neue inline Attributzugriffsfunktionen ersetzen die bisherige Verwendung von "history", wenn die "save-update" und andere Cascade-Operationen sich auf den gesamten Umfang der einem Attribut zugeordneten Datenmitglieder auswirken müssen. Dies reduziert den Overhead der Generierung eines neuen History-Objekts für diese geschwindigkeitskritische Operation.

  • Die Interna von ExecutionContext, dem Objekt, das einer Anweisungsausführung entspricht, wurden inline und vereinfacht.

  • Die von Typen für jede Anweisungsausführung generierten Callables bind_processor() und result_processor() werden nun für die Lebensdauer dieses Typs zwischengespeichert (sorgfältig, um Speicherlecks für Ad-hoc-Typen und Dialekte zu vermeiden), was den pro Anweisung anfallenden Aufruf-Overhead weiter reduziert.

  • Die Sammlung von "Bind-Prozessoren" für eine bestimmte Compiled-Instanz einer Anweisung wird ebenfalls auf dem Compiled-Objekt zwischengespeichert, wobei der "compiled cache" des Flush-Prozesses weiter genutzt wird, um dieselbe kompilierte Form von INSERT-, UPDATE- und DELETE-Anweisungen wiederzuverwenden.

Eine Demonstration der Reduzierung von Aufrufzahlen inklusive eines Beispiel-Benchmark-Skripts finden Sie unter https://techspot.org/2010/12/12/a-tale-of-three-profiles/

Kompositen neu geschrieben

Das "Composite"-Feature wurde neu geschrieben, ähnlich wie synonym() und comparable_property(), um eine leichtere Implementierung basierend auf Deskriptoren und Ereignissen zu verwenden, anstatt in die ORM-Interna einzubauen. Dies ermöglichte die Entfernung einiger Latenz aus den Mapper/Unit-of-Work-Interna und vereinfacht die Funktionsweise von Composite. Das Composite-Attribut verbirgt nun nicht mehr die zugrunde liegenden Spalten, auf denen es aufbaut, diese bleiben nun als normale Attribute erhalten. Komposite können auch als Proxy für relationship() sowie für Column()-Attribute fungieren.

Die wichtigste nicht abwärtskompatible Änderung bei Kompositen ist, dass sie das mutable=True-System zur Erkennung von In-Place-Mutationen nicht mehr verwenden. Bitte verwenden Sie die Mutationsverfolgungs-Erweiterung, um In-Place-Änderungsereignisse für die bestehende Composite-Verwendung einzurichten.

#2008 #2024

Kompaktere Form von query.join(target, onclause)

Die Standardmethode zum Ausgeben von query.join() zu einem Ziel mit einer expliziten onclause ist nun:

query.join(SomeClass, SomeClass.id == ParentClass.some_id)

In 0.6 galt diese Verwendung als Fehler, da join() mehrere Argumente akzeptiert, die mehreren JOIN-Klauseln entsprechen – die Zwei-Argumente-Form musste in einem Tupel sein, um zwischen Ein-Argument- und Zwei-Argument-Join-Zielen zu unterscheiden. Mitte 0.6 fügten wir eine Erkennung und eine Fehlermeldung für diesen spezifischen Aufrufstil hinzu, da er so häufig vorkam. In 0.7, da wir das exakte Muster sowieso erkennen und das Tippen eines Tupels ohne Grund extrem störend ist, wird die Nicht-Tupel-Methode nun zum "normalen" Weg, dies zu tun. Der Anwendungsfall für "mehrere JOINs" ist im Vergleich zum einzelnen Join-Fall extrem selten, und mehrere JOINs werden heutzutage klarer durch mehrere Aufrufe von join() dargestellt.

Die Tupelform bleibt aus Kompatibilitätsgründen erhalten.

Beachten Sie, dass alle anderen Formen von query.join() unverändert bleiben.

query.join(MyClass.somerelation)
query.join("somerelation")
query.join(MyTarget)
# ... etc

Abfragen mit Joins

#1923

Mutationsereigniserweiterung, ersetzt "mutable=True"

Eine neue Erweiterung, Mutationsverfolgung, bietet einen Mechanismus, durch den benutzerdefinierte Datentypen Änderungsereignisse an den oder die besitzenden Elternteile zurückgeben können. Die Erweiterung beinhaltet einen Ansatz für skalare Datenbankwerte, wie sie von PickleType, postgresql.ARRAY oder anderen benutzerdefinierten MutableType-Klassen verwaltet werden, sowie einen Ansatz für ORM "Kompositen", die mit composite() konfiguriert werden.

Siehe auch

Mutationsverfolgung

NULLS FIRST / NULLS LAST Operatoren

Diese werden als Erweiterung der Operatoren asc() und desc() implementiert und heißen nullsfirst() und nullslast().

#723

select.distinct(), query.distinct() akzeptiert *args für PostgreSQL DISTINCT ON

Dies war bereits durch Übergabe einer Liste von Ausdrücken an das Schlüsselwortargument distinct von select() verfügbar. Die Methode distinct() von select() und Query akzeptiert nun Positionsargumente, die als DISTINCT ON gerendert werden, wenn ein PostgreSQL-Backend verwendet wird.

distinct()

Query.distinct()

#1069

Index() kann inline innerhalb von Table, __table_args__ platziert werden

Das Index()-Konstrukt kann inline mit einer Tabellendefinition erstellt werden, wobei Zeichenketten als Spaltennamen verwendet werden, als Alternative zur Erstellung des Index außerhalb der Tabelle. Das heißt:

Table(
    "mytable",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("name", String(50), nullable=False),
    Index("idx_name", "name"),
)

Der Hauptgrund hierfür ist der Vorteil für deklarative __table_args__, insbesondere bei Verwendung mit Mixins.

class HasNameMixin(object):
    name = Column("name", String(50), nullable=False)

    @declared_attr
    def __table_args__(cls):
        return (Index("name"), {})


class User(HasNameMixin, Base):
    __tablename__ = "user"
    id = Column("id", Integer, primary_key=True)

Indizes

Fensternorm-SQL-Konstrukt

Eine "Fensternorm" liefert einer Anweisung Informationen über das Ergebnis-Set, während es produziert wird. Dies ermöglicht Kriterien gegen verschiedene Dinge wie "Reihennummer", "Rang" und so weiter. Sie sind bekannt dafür, zumindest von PostgreSQL, SQL Server und Oracle unterstützt zu werden, möglicherweise auch andere.

Die beste Einführung in Fensternormen findet sich auf der PostgreSQL-Website, wo Fensternormen seit Version 8.4 unterstützt werden.

https://www.postgresql.org/docs/current/static/tutorial-window.html

SQLAlchemy bietet ein einfaches Konstrukt, das typischerweise über eine bestehende Funktionsklausel aufgerufen wird, unter Verwendung der Methode over(), die die Schlüsselwortargumente order_by und partition_by akzeptiert. Unten replizieren wir das erste Beispiel im PG-Tutorial.

from sqlalchemy.sql import table, column, select, func

empsalary = table("empsalary", column("depname"), column("empno"), column("salary"))

s = select(
    [
        empsalary,
        func.avg(empsalary.c.salary)
        .over(partition_by=empsalary.c.depname)
        .label("avg"),
    ]
)

print(s)

SQL

SELECT empsalary.depname, empsalary.empno, empsalary.salary,
avg(empsalary.salary) OVER (PARTITION BY empsalary.depname) AS avg
FROM empsalary

sqlalchemy.sql.expression.over

#1844

execution_options() auf Connection akzeptiert das Argument "isolation_level"

Dies setzt die Transaktionsisolationsstufe für eine einzelne Connection, bis diese Connection geschlossen und ihre zugrunde liegende DBAPI-Ressource an den Connection-Pool zurückgegeben wird, woraufhin die Isolationsstufe auf den Standard zurückgesetzt wird. Die Standard-Isolationsstufe wird mit dem Argument isolation_level für create_engine() festgelegt.

Die Unterstützung der Transaktionsisolation wird derzeit nur von den PostgreSQL- und SQLite-Backends unterstützt.

execution_options()

#2001

TypeDecorator funktioniert mit ganzzahligen Primärschlüsselspalten

Ein TypeDecorator, der das Verhalten von Integer erweitert, kann mit einer Primärschlüsselspalte verwendet werden. Das "autoincrement"-Feature von Column erkennt nun, dass die zugrunde liegende Datenbankspalte immer noch eine Ganzzahl ist, so dass lastrowid-Mechanismen weiterhin funktionieren. Der TypeDecorator selbst wendet seinen Ergebniswert-Prozessor auf neu generierte Primärschlüssel an, einschließlich derer, die vom DBAPI cursor.lastrowid-Accessor empfangen werden.

#2005 #2006

TypeDecorator ist im Importbereich "sqlalchemy" vorhanden

Es ist nicht mehr notwendig, dies aus sqlalchemy.types zu importieren, es wird nun in sqlalchemy gespiegelt.

Neue Dialekte

Dialekte wurden hinzugefügt

  • ein MySQLdb-Treiber für die Drizzle-Datenbank

    Drizzle

  • Unterstützung für den pymysql DBAPI

    pymsql Hinweise

  • psycopg2 funktioniert jetzt mit Python 3

Verhaltensänderungen (Abwärtskompatibel)

C-Erweiterungen standardmäßig kompiliert

Dies gilt ab 0.7b4. Die Erweiterungen werden kompiliert, wenn cPython 2.xx erkannt wird. Wenn die Kompilierung fehlschlägt, z.B. bei einer Windows-Installation, wird diese Bedingung abgefangen und die Nicht-C-Installation wird fortgesetzt. Die C-Erweiterungen werden nicht kompiliert, wenn Python 3 oder PyPy verwendet wird.

Query.count() vereinfacht, sollte praktisch immer funktionieren

Das sehr alte Raten, das innerhalb von Query.count() stattfand, wurde modernisiert, um .from_self() zu verwenden. Das heißt, query.count() ist nun äquivalent zu:

query.from_self(func.count(literal_column("1"))).scalar()

Zuvor versuchte die interne Logik, die Spaltenklausel der Abfrage selbst neu zu schreiben, und bei Erkennung einer "Subquery"-Bedingung, wie z.B. einer spaltenbasierten Abfrage, die Aggregate enthalten könnte, oder einer Abfrage mit DISTINCT, würde sie einen komplizierten Prozess des Neuschreibens der Spaltenklausel durchlaufen. Diese Logik schlug bei komplexen Bedingungen fehl, insbesondere bei solchen, die Joined Table Inheritance betrafen, und war längst durch den umfassenderen Aufruf .from_self() überflüssig geworden.

Die von query.count() ausgegebene SQL-Anweisung hat nun immer die Form:

SELECT count(1) AS count_1 FROM (
    SELECT user.id AS user_id, user.name AS user_name from user
) AS anon_1

Das heißt, die ursprüngliche Abfrage wird vollständig in einer Subquery erhalten, ohne dass noch geraten werden muss, wie gezählt werden soll.

#2093

Um eine Nicht-Subquery-Form von count() auszugeben

MySQL-Benutzer haben bereits berichtet, dass die MyISAM-Engine bei dieser einfachen Änderung erwartungsgemäß komplett zusammenbricht. Beachten Sie, dass für ein einfaches count(), das für DBs optimiert wird, die keine einfachen Subqueries verarbeiten können, func.count() verwendet werden sollte.

from sqlalchemy import func

session.query(func.count(MyClass.id)).scalar()

oder für count(*)

from sqlalchemy import func, literal_column

session.query(func.count(literal_column("*"))).select_from(MyClass).scalar()

LIMIT/OFFSET-Klauseln verwenden jetzt Bindungsparameter

Die LIMIT- und OFFSET-Klauseln oder ihre Backend-Äquivalente (d.h. TOP, ROW NUMBER OVER, etc.) verwenden Bindungsparameter für die tatsächlichen Werte, für alle Backends, die dies unterstützen (die meisten außer Sybase). Dies ermöglicht eine bessere Leistung des Abfrageoptimierers, da die Textzeichenkette für mehrere Anweisungen mit unterschiedlichen LIMIT/OFFSET identisch ist.

#805

Protokollierungsverbesserungen

Vinay Sajip hat einen Patch für unser Protokollierungssystem bereitgestellt, sodass der "Hex-String", der in Protokollanweisungen für Engines und Pools eingebettet ist, nicht mehr benötigt wird, um das korrekte Funktionieren des echo-Flags zu ermöglichen. Ein neues System, das gefilterte Protokollobjekte verwendet, ermöglicht es uns, unser aktuelles Verhalten beizubehalten, dass echo lokal für einzelne Engines ist, ohne dass zusätzliche identifizierende Zeichenketten lokal für diese Engines erforderlich sind.

#1926

Vereinfachte Zuweisung von polymorphic_on

Die Population der polymorphic_on-Spalten-abgebildeten Attributs tritt bei Verwendung in einem Vererbungsszenario nun auf, wenn das Objekt konstruiert wird, d.h. seine __init__-Methode aufgerufen wird, unter Verwendung des init-Ereignisses. Das Attribut verhält sich dann wie jedes andere spalten-abgebildete Attribut. Zuvor wurde während des Flushs eine spezielle Logik ausgelöst, um diese Spalte zu füllen, was verhinderte, dass benutzerdefinierter Code ihr Verhalten ändern konnte. Der neue Ansatz verbessert dies auf drei Arten: 1. Die polymorphe Identität ist nun auf dem Objekt vorhanden, sobald es konstruiert ist; 2. Die polymorphe Identität kann von Benutzercode geändert werden, ohne Verhaltensunterschiede zu anderen spalten-abgebildeten Attributen; 3. Die Interna des Mappers während des Flushs sind vereinfacht und müssen keine speziellen Prüfungen mehr für diese Spalte durchführen.

#1895

contains_eager() verkettet sich über mehrere Pfade (z.B. "all()")

Der Modifikator `contains_eager()`` wird sich nun selbst über einen längeren Pfad verkettet, ohne dass einzelne ``contains_eager()`-Aufrufe ausgegeben werden müssen. Stattdessen

session.query(A).options(contains_eager(A.b), contains_eager(A.b, B.c))

können Sie sagen

session.query(A).options(contains_eager(A.b, B.c))

#2032

Das Flushen von Waisen, die keinen Elternteil haben, ist erlaubt

Wir hatten ein langjähriges Verhalten, das während des Flushs nach einem sogenannten "Waisen" prüft, d.h. ein Objekt, das mit einer relationship() assoziiert ist, die "delete-orphan" Kaskade angibt, neu zur Sitzung für einen INSERT hinzugefügt wurde und für das keine Elternbeziehung hergestellt wurde. Diese Prüfung wurde vor Jahren hinzugefügt, um einige Testfälle zu unterstützen, die das Waisenverhalten auf Konsistenz prüften. Im modernen SQLA ist diese Prüfung auf der Python-Seite nicht mehr nötig. Das äquivalente Verhalten der "Waisenprüfung" wird erreicht, indem die Fremdschlüsselreferenz auf die Elternzeile des Objekts NOT NULL gemacht wird, wo die Datenbank ihre Aufgabe erfüllt, die Datenkonsistenz herzustellen, genauso wie SQLA die meisten anderen Operationen zulässt. Wenn der Fremdschlüssel des Elternteils des Objekts nullfähig ist, kann die Zeile eingefügt werden. Das "Waisen"-Verhalten tritt auf, wenn das Objekt mit einem bestimmten Elternteil persistent gemacht wurde und dann von diesem Elternteil gelöst wird, was zur Ausgabe einer DELETE-Anweisung für es führt.

#1912

Warnungen generiert, wenn Sammlungsmitglieder, skalare Referenten nicht Teil des Flushs sind

Warnungen werden nun ausgegeben, wenn verwandte Objekte, auf die über eine geladene relationship() auf einem als "dirty" markierten Elternobjekt verwiesen wird, nicht in der aktuellen Session vorhanden sind.

Die save-update-Kaskade tritt in Kraft, wenn Objekte zur Session hinzugefügt werden, oder wenn Objekte zum ersten Mal mit einem Elternteil assoziiert werden, sodass ein Objekt und alles, was damit zusammenhängt, normalerweise alle in derselben Session vorhanden sind. Wenn jedoch die save-update-Kaskade für eine bestimmte relationship() deaktiviert ist, tritt dieses Verhalten nicht auf und der Flush-Prozess versucht nicht, dies zu korrigieren, sondern bleibt konsistent mit dem konfigurierten Kaskadenverhalten. Zuvor wurden solche Objekte, wenn sie während des Flushs erkannt wurden, stillschweigend übersprungen. Das neue Verhalten ist, dass eine Warnung ausgegeben wird, um auf eine Situation hinzuweisen, die häufig die Ursache für unerwartetes Verhalten ist.

#1973

Setup installiert kein Nose-Plugin mehr

Seit wir zu Nose gewechselt sind, verwenden wir ein Plugin, das über setuptools installiert wird, so dass das Skript nosetests automatisch den SQLA-Plugin-Code ausführt, der für unsere Tests zur Bereitstellung einer vollständigen Umgebung notwendig ist. Mitte 0.6 erkannten wir, dass das Importmuster hier bedeutete, dass das "coverage"-Plugin von Nose fehlschlagen würde, da "coverage" erfordert, dass es gestartet wird, bevor irgendein abzudeckendes Modul importiert wird; Mitte 0.6 haben wir die Situation verschlimmert, indem wir ein separates Paket sqlalchemy-nose zum Build hinzugefügt haben, um dies zu überwinden.

In 0.7 haben wir darauf verzichtet, dass nosetests automatisch funktioniert, da das SQLAlchemy-Modul eine große Anzahl von Nose-Konfigurationsoptionen für alle Verwendungen von nosetests erzeugen würde, nicht nur für die SQLAlchemy-Unit-Tests selbst, und die zusätzliche Installation von sqlalchemy-nose war eine noch schlechtere Idee und erzeugte ein zusätzliches Paket in Python-Umgebungen. Das Skript sqla_nose.py in 0.7 ist nun der einzige Weg, die Tests mit Nose auszuführen.

#1949

Nicht-Table-abgeleitete Konstrukte können abgebildet werden

Ein Konstrukt, das überhaupt nicht auf einer Table basiert, wie z.B. eine Funktion, kann abgebildet werden.

from sqlalchemy import select, func
from sqlalchemy.orm import mapper


class Subset(object):
    pass


selectable = select(["x", "y", "z"]).select_from(func.some_db_function()).alias()
mapper(Subset, selectable, primary_key=[selectable.c.x])

#1876

aliased() akzeptiert FromClause-Elemente

Dies ist ein praktischer Helfer, sodass im Falle, dass eine einfache FromClause, wie z.B. ein select, Table oder join an das orm.aliased()-Konstrukt übergeben wird, dieses an die Methode .alias() dieses From-Konstrukts weitergegeben wird, anstatt eine ORM-Level AliasedClass zu konstruieren.

#2018

Session.connection(), Session.execute() akzeptieren ‘bind’

Dies ermöglicht es Execute/Connection-Operationen, explizit an der offenen Transaktion einer Engine teilzunehmen. Es ermöglicht auch benutzerdefinierte Unterklassen von Session, die ihre eigene get_bind() Methode und Argumente implementieren, diese benutzerdefinierten Argumente sowohl mit der execute() als auch mit der connection() Methode gleichermaßen zu verwenden.

Session.connection Session.execute

#1996

Eigenständige Bindungsparameter in der Spaltenklausel werden automatisch benannt.

Bindungsparameter, die in der "Spaltenklausel" eines SELECT-Statements vorhanden sind, werden jetzt wie andere "anonyme" Klauseln automatisch benannt. Dies ermöglicht unter anderem, dass ihr "Typ" aussagekräftig ist, wenn die Zeile abgerufen wird, wie z. B. bei Zeilenverarbeitungsprogrammen für Ergebnisse.

SQLite – relative Dateipfade werden über os.path.abspath() normalisiert

Dies geschieht, damit ein Skript, das das aktuelle Verzeichnis ändert, weiterhin denselben Speicherort anvisiert, wie wenn nachfolgende SQLite-Verbindungen hergestellt werden.

#2036

MS-SQL – String/Unicode/VARCHAR/NVARCHAR/VARBINARY geben "max" für keine Länge aus

Auf dem MS-SQL-Backend geben die Typen String/Unicode und ihre Gegenstücke VARCHAR/NVARCHAR sowie VARBINARY (#1833) "max" als Länge aus, wenn keine Länge angegeben ist. Dies macht sie kompatibler mit dem VARCHAR-Typ von PostgreSQL, der ebenfalls unbegrenzt ist, wenn keine Länge angegeben ist. SQL Server weist diesen Typen standardmäßig die Länge '1' zu, wenn keine Länge angegeben ist.

Verhaltensänderungen (rückwärtskompatibel inkompatibel)

Beachten Sie nochmals, dass abgesehen von der Standardänderung der Änderbarkeit die meisten dieser Änderungen *extrem geringfügig* sind und die meisten Benutzer nicht betreffen werden.

PickleType und ARRAY-Änderbarkeit standardmäßig deaktiviert

Diese Änderung bezieht sich auf das Standardverhalten der ORM beim Mapping von Spalten, die entweder die Datentypen PickleType oder postgresql.ARRAY haben. Das Flag mutable ist jetzt standardmäßig auf False gesetzt. Wenn eine bestehende Anwendung diese Typen verwendet und auf die Erkennung von In-Place-Änderungen angewiesen ist, muss das Typobjekt mit mutable=True konstruiert werden, um das Verhalten von 0.6 wiederherzustellen.

Table(
    "mytable",
    metadata,
    # ....
    Column("pickled_data", PickleType(mutable=True)),
)

Das Flag mutable=True wird zugunsten der neuen Mutationsverfolgungserweiterung ausläuft. Diese Erweiterung bietet einen Mechanismus, mit dem benutzerdefinierte Datentypen Änderungsereignisse zurück an den oder die besitzenden Elternteile senden können.

Der vorherige Ansatz mit mutable=True bietet keine Ereignisse für Änderungen – stattdessen muss die ORM alle in einer Sitzung vorhandenen veränderbaren Werte durchsuchen und sie mit ihrem ursprünglichen Wert vergleichen, wann immer flush() aufgerufen wird, was ein sehr zeitaufwendiges Ereignis ist. Dies ist ein Überbleibsel aus der Anfangszeit von SQLAlchemy, als flush() nicht automatisch war und das System zur Nachverfolgung der Historie bei weitem nicht so ausgefeilt war wie heute.

Bestehende Anwendungen, die PickleType, postgresql.ARRAY oder andere MutableType-Unterklassen verwenden und In-Place-Mutationserkennung erfordern, sollten auf das neue Mutationsverfolgungssystem migrieren, da mutable=True wahrscheinlich in Zukunft veraltet sein wird.

#1980

Mutationserkennung von composite() erfordert die Mutationsverfolgungserweiterung

Sogenannte "komposite" zugeordnete Attribute, die mit der unter Composite Column Types beschriebenen Technik konfiguriert wurden, wurden neu implementiert, sodass die ORM-Interna sie nicht mehr kennen (was zu kürzeren und effizienteren Codepfaden in kritischen Abschnitten führt). Während Komposittypen im Allgemeinen als unveränderliche Wertobjekte behandelt werden sollen, wurde dies nie erzwungen. Für Anwendungen, die Komposita mit Änderbarkeit verwenden, bietet die Mutationsverfolgungserweiterung eine Basisklasse, die einen Mechanismus für benutzerdefinierte Komposittypen einrichtet, um Änderungsereignisnachrichten an den oder die besitzenden Elternteile jedes Objekts zurückzusenden.

Anwendungen, die Komposittypen verwenden und auf die In-Place-Mutationserkennung dieser Objekte angewiesen sind, sollten entweder auf die Erweiterung "Mutationsverfolgung" migrieren oder die Verwendung der Komposittypen so ändern, dass In-Place-Änderungen nicht mehr erforderlich sind (d. h. sie als unveränderliche Wertobjekte behandeln).

SQLite – das SQLite-Dialekt verwendet nun NullPool für dateibasierte Datenbanken

Diese Änderung ist **99,999% abwärtskompatibel**, es sei denn, Sie verwenden temporäre Tabellen über Verbindungen im Verbindungspool.

Eine dateibasierte SQLite-Verbindung ist blitzschnell, und die Verwendung von NullPool bedeutet, dass jeder Aufruf von Engine.connect eine neue pysqlite-Verbindung erstellt.

Zuvor wurde der SingletonThreadPool verwendet, was bedeutete, dass alle Verbindungen zu einer bestimmten Engine in einem Thread dieselbe Verbindung waren. Die neue Methode ist intuitiver, insbesondere wenn mehrere Verbindungen verwendet werden.

SingletonThreadPool ist weiterhin der Standard-Engine, wenn eine `:memory:`-Datenbank verwendet wird.

Beachten Sie, dass diese Änderung **temporäre Tabellen, die über Session-Commits hinaus verwendet werden, bricht**, aufgrund der Art und Weise, wie SQLite temporäre Tabellen behandelt. Sehen Sie die Notiz unter https://sqlalchemy.de/docs/dialects/sqlite.html#using- temporäre-tabellen-mit-sqlite, wenn temporäre Tabellen über den Geltungsbereich einer Pool-Verbindung hinaus gewünscht sind.

#1921

Session.merge() prüft Versions-IDs für versionierte Mapper

Session.merge() prüft die Versions-ID des eingehenden Zustands gegen die der Datenbank, vorausgesetzt, das Mapping verwendet Versions-IDs und der eingehende Zustand hat eine Version_id zugewiesen. Es wird dann StaleDataError ausgelöst, wenn sie nicht übereinstimmen. Dies ist das korrekte Verhalten, denn wenn der eingehende Zustand eine veraltete Versions-ID enthält, sollte davon ausgegangen werden, dass der Zustand veraltet ist.

Wenn Daten in einen versionierten Zustand zusammengeführt werden, kann das Versions-ID-Attribut undefiniert bleiben, und es wird keine Versionsprüfung durchgeführt.

Diese Prüfung wurde durch die Untersuchung dessen bestätigt, was Hibernate tut – sowohl die merge()- als auch die Versionierungsfunktionen wurden ursprünglich von Hibernate übernommen.

#2027

Tuple-Labelnamen in Query verbessert

Diese Verbesserung ist potenziell leicht rückwärtskompatibel für Anwendungen, die sich auf das alte Verhalten verlassen haben.

Gegeben seien zwei zugeordnete Klassen Foo und Bar, die jeweils eine Spalte spam haben.

qa = session.query(Foo.spam)
qb = session.query(Bar.spam)

qu = qa.union(qb)

Der Name, der der einzelnen Spalte zugeordnet wird, die von qu geliefert wird, ist spam. Zuvor wäre dies aufgrund der Art und Weise, wie die union Dinge kombinierte, etwas wie foo_spam gewesen, was inkonsistent mit dem Namen spam im Falle einer nicht unionierten Abfrage ist.

#1942

Gemappte Spaltenattribute verweisen zuerst auf die spezifischste Spalte

Dies ist eine Änderung des Verhaltens, das auftritt, wenn ein gemapptes Spaltenattribut auf mehrere Spalten verweist, insbesondere wenn es sich um ein Attribut einer Joined-Table-Unterklasse handelt, das denselben Namen wie ein Attribut der Oberklasse hat.

Bei Verwendung von Declarative ist das Szenario wie folgt:

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)


class Child(Parent):
    __tablename__ = "child"
    id = Column(Integer, ForeignKey("parent.id"), primary_key=True)

Oben verweist das Attribut Child.id aufgrund des Namens des Attributs sowohl auf die Spalte child.id als auch auf parent.id. Wenn es auf der Klasse anders benannt wäre, z. B. Child.child_id, würde es dann eindeutig auf child.id abgebildet, wobei Child.id dasselbe Attribut wie Parent.id wäre.

Wenn das Attribut id dazu gebracht wird, sowohl auf parent.id als auch auf child.id zu verweisen, werden diese in einer geordneten Liste gespeichert. Ein Ausdruck wie Child.id verweist dann nur auf *eine* dieser Spalten, wenn er gerendert wird. Bis 0.6 war dies die Spalte parent.id. In 0.7 ist es die weniger überraschende child.id.

Das Erbe dieses Verhaltens beruht auf Verhaltensweisen und Einschränkungen der ORM, die nicht mehr wirklich relevant sind; alles, was benötigt wurde, war die Umkehrung der Reihenfolge.

Ein Hauptvorteil dieses Ansatzes ist, dass es jetzt einfacher ist, primaryjoin-Ausdrücke zu erstellen, die auf die lokale Spalte verweisen.

class Child(Parent):
    __tablename__ = "child"
    id = Column(Integer, ForeignKey("parent.id"), primary_key=True)
    some_related = relationship(
        "SomeRelated", primaryjoin="Child.id==SomeRelated.child_id"
    )


class SomeRelated(Base):
    __tablename__ = "some_related"
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey("child.id"))

Vor 0.7 würde der Ausdruck Child.id auf Parent.id verweisen, und es wäre notwendig, child.id einem separaten Attribut zuzuordnen.

Dies bedeutet auch, dass sich eine Abfrage wie diese ändert:

session.query(Parent).filter(Child.id > 7)

In 0.6 würde dies gerendert:

SELECT parent.id AS parent_id
FROM parent
WHERE parent.id > :id_1

In 0.7 erhalten Sie:

SELECT parent.id AS parent_id
FROM parent, child
WHERE child.id > :id_1

was, wie Sie bemerken werden, ein kartesisches Produkt ist – dieses Verhalten ist jetzt äquivalent zu dem jedes anderen Attributs, das lokal zu Child ist. Die Methode with_polymorphic() oder eine ähnliche Strategie des expliziten Verknüpfens der zugrunde liegenden Table-Objekte wird verwendet, um eine Abfrage gegen alle Parent-Objekte mit Kriterien gegen Child zu rendern, auf die gleiche Weise wie in 0.5 und 0.6.

print(s.query(Parent).with_polymorphic([Child]).filter(Child.id > 7))

Was auf beiden Versionen, 0.6 und 0.7, gerendert wird:

SELECT parent.id AS parent_id, child.id AS child_id
FROM parent LEFT OUTER JOIN child ON parent.id = child.id
WHERE child.id > :id_1

Ein weiterer Effekt dieser Änderung ist, dass eine joined-inheritance-Ladung über zwei Tabellen aus dem Wert der Kindertabelle und nicht aus dem der Eltern-Tabelle gefüllt wird. Ein ungewöhnlicher Fall ist, dass eine Abfrage gegen "Parent" mit with_polymorphic="*" eine Abfrage gegen "parent" mit einem LEFT OUTER JOIN zu "child" ausgibt. Die Zeile befindet sich in "Parent", sieht, dass die polymorphe Identität zu "Child" korrespondiert, aber nehmen wir an, die tatsächliche Zeile in "child" wurde *gelöscht*. Aufgrund dieser Beschädigung kommt die Zeile mit allen Spalten, die zu "child" gehören, mit NULL-Werten – dies ist nun der Wert, der gefüllt wird, nicht der in der Elterntabelle.

#1892

Mapping auf Joins mit zwei oder mehr gleichnamigen Spalten erfordert explizite Deklaration

Dies ist in gewisser Weise mit der vorherigen Änderung in #1892 verwandt. Beim Mapping auf einen Join müssen gleichnamige Spalten explizit mit gemappten Attributen verknüpft werden, d. h. wie unter Mapping a Class Against Multiple Tables beschrieben.

Gegeben seien zwei Tabellen foo und bar, die jeweils eine Primärschlüsselspalte id haben. Das Folgende führt nun zu einem Fehler:

foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
mapper(FooBar, foobar)

Dies liegt daran, dass der mapper() sich weigert zu erraten, welche Spalte die primäre Darstellung von FooBar.id ist – ist es foo.c.id oder ist es bar.c.id? Das Attribut muss explizit sein.

foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
mapper(FooBar, foobar, properties={"id": [foo.c.id, bar.c.id]})

#1896

Mapper erfordert, dass die Spalte polymorphic_on im gemappten wählbaren Element vorhanden ist

Dies ist eine Warnung in 0.6, jetzt ein Fehler in 0.7. Die für polymorphic_on angegebene Spalte muss sich im zugeordneten wählbaren Element befinden. Dies soll gelegentliche Benutzerfehler verhindern, wie zum Beispiel:

mapper(SomeClass, sometable, polymorphic_on=some_lookup_table.c.id)

wobei polymorphic_on sich auf eine sometable-Spalte beziehen müsste, in diesem Fall vielleicht sometable.c.some_lookup_id. Es gibt auch einige "polymorphe Union"-Szenarien, bei denen ähnliche Fehler manchmal auftreten.

Eine solche Konfigurationsfehlkonfiguration war schon immer "falsch", und das obige Mapping funktioniert nicht wie angegeben – die Spalte würde ignoriert. Es ist jedoch potenziell rückwärtskompatibel in dem seltenen Fall, dass eine Anwendung unwissentlich auf diesem Verhalten beruht hat.

#1875

DDL()-Konstrukte maskieren jetzt Prozentzeichen

Zuvor mussten Prozentzeichen in DDL()-Strings je nach DBAPI maskiert werden, d. h. %%, für DBAPIs, die pyformat oder format-Bindungen akzeptieren (d. h. psycopg2, mysql-python), was im Gegensatz zu text()-Konstrukten inkonsistent war, die dies automatisch taten. Dieselbe Maskierung erfolgt nun für DDL() wie für text().

#1897

Table.c / MetaData.tables leicht verfeinert, erlauben keine direkte Mutation mehr

Ein weiterer Bereich, in dem einige Benutzer auf eine Weise manipulierten, die tatsächlich nicht wie erwartet funktionierte, aber dennoch eine extrem geringe Chance hinterließ, dass eine Anwendung auf diesem Verhalten beruhte: Das von dem Attribut .c von Table und dem Attribut .tables von MetaData zurückgegebene Konstrukt ist explizit nicht veränderbar. Die "veränderbare" Version des Konstrukts ist nun privat. Das Hinzufügen von Spalten zu .c erfolgt über die Methode append_column() von Table, die sicherstellt, dass die Dinge auf die richtige Weise mit der übergeordneten Table verknüpft sind; ebenso hat MetaData.tables einen Vertrag mit den Table-Objekten, die in diesem Dictionary gespeichert sind, sowie ein wenig neue Buchführung, da ein set() aller Schemanamen verfolgt wird, was nur durch die Verwendung des öffentlichen Table-Konstruktors sowie Table.tometadata() erfüllt wird.

Es ist natürlich möglich, dass die von diesen Attributen konsultierten Sammlungen ColumnCollection und dict eines Tages Ereignisse für alle ihre mutationsbezogenen Methoden implementieren könnten, sodass die entsprechende Buchführung bei direkter Mutation der Sammlungen erfolgt. Aber bis jemand die Motivation hat, all dies zusammen mit Dutzenden neuer Unit-Tests zu implementieren, wird die Einschränkung der Wege zur Mutation dieser Sammlungen sicherstellen, dass keine Anwendung versucht, auf Verwendungen zu setzen, die derzeit nicht unterstützt werden.

#1893 #1917

server_default gibt konsistent None für alle inserted_primary_key-Werte zurück

Konsistenz wurde hergestellt, wenn server_default auf einer Integer-PK-Spalte vorhanden ist. SQLA ruft diese nicht vorab ab, noch kommen sie in cursor.lastrowid (DBAPI) zurück. Sichergestellt, dass alle Backends in result.inserted_primary_key für diese konsistent None zurückgeben – einige Backends haben möglicherweise zuvor einen Wert zurückgegeben. Die Verwendung eines server_default auf einer Primärschlüsselspalte ist extrem ungewöhnlich. Wenn eine spezielle Funktion oder ein SQL-Ausdruck zur Generierung von Primärschlüssel-Defaults verwendet wird, sollte dies als Python-seitiger "Default" anstelle von server_default festgelegt werden.

Bezüglich der Reflexion für diesen Fall setzt die Reflexion einer Int-PK-Spalte mit einem server_default das Flag "autoincrement" auf False, außer im Fall einer PG SERIAL-Spalte, bei der wir ein Sequenz-Default erkannt haben.

#2020 #2021

Der Alias sqlalchemy.exceptions in sys.modules wird entfernt

Seit einigen Jahren haben wir den String sqlalchemy.exceptions in sys.modules hinzugefügt, damit eine Anweisung wie "import sqlalchemy.exceptions" funktioniert. Der Name des Kern-Ausnahmemoduls ist seit langem exc, daher ist der empfohlene Import für dieses Modul:

from sqlalchemy import exc

Der Name exceptions ist immer noch in "sqlalchemy" vorhanden für Anwendungen, die möglicherweise "from sqlalchemy import exceptions" gesagt haben, aber sie sollten auch beginnen, den Namen exc zu verwenden.

Änderungen am Query-Timing-Rezept

Obwohl nicht Teil von SQLAlchemy selbst, ist es erwähnenswert, dass die Überarbeitung des ConnectionProxy in das neue Ereignissystem bedeutet, dass er für das Rezept "Alle Abfragen zeitlich messen" nicht mehr geeignet ist. Bitte passen Sie die Abfrage-Timer an, um die Ereignisse before_cursor_execute() und after_cursor_execute() zu verwenden, wie im aktualisierten Rezept UsageRecipes/Profiling gezeigt.

Veraltete API

Standardkonstruktor für Typen akzeptiert keine Argumente mehr

Einfache Typen wie Integer, Date usw. im Modul für Kerntypen akzeptieren keine Argumente. Der Standardkonstruktor, der eine Sammel-*args, **kwargs akzeptiert/ignoriert, wird ab 0.7b4/0.7.0 wiederhergestellt, gibt aber eine Deprecation-Warnung aus.

Wenn Argumente mit einem Kerntyp wie Integer verwendet werden, beabsichtigen Sie möglicherweise, einen dialektspezifischen Typ zu verwenden, z. B. sqlalchemy.dialects.mysql.INTEGER, der beispielsweise ein "display_width"-Argument akzeptiert.

compile_mappers() umbenannt in configure_mappers(), Konfigurationsinterna vereinfacht

Dieses System entwickelte sich langsam von etwas Kleinem, das lokal für einen einzelnen Mapper implementiert und schlecht benannt war, zu etwas, das eher eine globale Funktion auf "Registry"-Ebene ist und schlecht benannt wurde. Daher haben wir beides behoben, indem wir die Implementierung vollständig aus Mapper entfernt und sie in configure_mappers() umbenannt haben. Für eine Anwendung ist es natürlich normalerweise nicht erforderlich, configure_mappers() aufzurufen, da dieser Prozess nach Bedarf erfolgt, sobald die Mappings über Attribut- oder Abfragezugriff benötigt werden.

#1966

Core Listener/Proxy durch Ereignis-Listener abgelöst

PoolListener, ConnectionProxy und DDLElement.execute_at werden durch event.listen() unter Verwendung der Dispatch-Ziele PoolEvents, EngineEvents bzw. DDLEvents abgelöst.

ORM-Erweiterungen durch Ereignis-Listener abgelöst

MapperExtension, AttributeExtension und SessionExtension werden durch event.listen() unter Verwendung der Dispatch-Ziele MapperEvents/InstanceEvents, AttributeEvents bzw. SessionEvents abgelöst.

Das Senden eines Strings an 'distinct' in select() für MySQL sollte über Präfixe erfolgen

Dieses obskure Feature ermöglicht dieses Muster mit dem MySQL-Backend:

select([mytable], distinct="ALL", prefixes=["HIGH_PRIORITY"])

Das Schlüsselwort prefixes oder die Methode prefix_with() sollten für nicht standardmäßige oder ungewöhnliche Präfixe verwendet werden.

select([mytable]).prefix_with("HIGH_PRIORITY", "ALL")

useexisting abgelöst durch extend_existing und keep_existing

Das Flag useexisting auf Table wurde durch ein neues Paar von Flags ersetzt: keep_existing und extend_existing. extend_existing ist äquivalent zu useexisting – die bestehende Table wird zurückgegeben, und zusätzliche Konstruktionselemente werden hinzugefügt. Bei keep_existing wird die bestehende Table zurückgegeben, aber es werden keine zusätzlichen Konstruktionselemente hinzugefügt – diese Elemente werden nur angewendet, wenn die Table neu erstellt wird.

Rückwärts inkompatible API-Änderungen

An bindparam() übergebene Callables werden nicht ausgewertet – beeinflusst das Beaker-Beispiel

#1950

Beachten Sie, dass dies das Beaker Caching-Beispiel betrifft, bei dem die Funktionsweise der Funktion _params_from_query() eine leichte Anpassung erforderte. Wenn Sie Code aus dem Beaker-Beispiel verwenden, sollte diese Änderung angewendet werden.

types.type_map ist jetzt privat, types._type_map

Wir haben festgestellt, dass einige Benutzer dieses Dictionary innerhalb von sqlalchemy.types als Abkürzung zum Verknüpfen von Python-Typen mit SQL-Typen nutzten. Wir können den Inhalt oder das Format dieses Dictionaries nicht garantieren, und darüber hinaus gibt es bei der Zuordnung von Python-Typen in einer Eins-zu-Eins-Beziehung einige Graubereiche, die am besten von einzelnen Anwendungen entschieden werden sollten. Daher haben wir dieses Attribut unterstrichen.

#1870

Das Schlüsselwortargument alias der eigenständigen Funktion alias() wurde in name umbenannt

Dies geschieht, damit das Schlüsselwortargument name mit dem der alias()-Methoden auf allen FromClause-Objekten sowie dem name-Argument von Query.subquery() übereinstimmt.

Nur Code, der die eigenständige Funktion alias() und nicht die gebundenen Methoden verwendet und den Aliasnamen über den expliziten Schlüsselwortnamen alias und nicht positional übergibt, müsste hier geändert werden.

Nicht-öffentliche Pool-Methoden unterstrichen

Alle Methoden von Pool und dessen Unterklassen, die nicht für die öffentliche Nutzung bestimmt sind, wurden mit Unterstrichen umbenannt. Dass sie zuvor nicht so benannt waren, war ein Fehler.

Pooling-Methoden nun unterstrichen oder entfernt

Pool.create_connection() -> Pool._create_connection()

Pool.do_get() -> Pool._do_get()

Pool.do_return_conn() -> Pool._do_return_conn()

Pool.do_return_invalid() -> entfernt, wurde nicht verwendet

Pool.return_conn() -> Pool._return_conn()

Pool.get() -> Pool._get(), öffentliche API ist Pool.connect()

SingletonThreadPool.cleanup() -> _cleanup()

SingletonThreadPool.dispose_local() -> entfernt, verwenden Sie conn.invalidate()

#1982

Zuvor veraltet, jetzt entfernt

Query.join(), Query.outerjoin(), eagerload(), eagerload_all(), andere erlauben keine Listen von Attributen mehr als Argumente

Das Übergeben einer Liste von Attributen oder Attributnamen an Query.join, eagerload() und ähnliche Methoden wurde seit 0.5 als veraltet markiert.

# old way, deprecated since 0.5
session.query(Houses).join([Houses.rooms, Room.closets])
session.query(Houses).options(eagerload_all([Houses.rooms, Room.closets]))

Diese Methoden akzeptieren alle *args ab der 0.5er-Serie.

# current way, in place since 0.5
session.query(Houses).join(Houses.rooms, Room.closets)
session.query(Houses).options(eagerload_all(Houses.rooms, Room.closets))

ScopedSession.mapper wird entfernt

Diese Funktion bot eine Mapper-Erweiterung, die klassenbasierte Funktionalität mit einer bestimmten ScopedSession verknüpfte und insbesondere das Verhalten bot, dass neue Objektinstanzen automatisch mit dieser Sitzung verknüpft wurden. Die Funktion wurde von Tutorials und Frameworks überstrapaziert, was aufgrund ihres impliziten Verhaltens zu großer Verwirrung bei den Benutzern führte und in Version 0.5.5 als veraltet eingestuft wurde. Techniken zur Replikation ihrer Funktionalität finden Sie unter [wiki:UsageRecipes/SessionAwareMapper]