Was gibt es Neues in SQLAlchemy 0.5?

Über dieses Dokument

Dieses Dokument beschreibt die Änderungen zwischen SQLAlchemy Version 0.4, zuletzt veröffentlicht am 12. Oktober 2008, und SQLAlchemy Version 0.5, zuletzt veröffentlicht am 16. Januar 2010.

Dokumentdatum: 4. August 2009

Diese Anleitung dokumentiert API-Änderungen, die Benutzer betrifft, die ihre Anwendungen von der SQLAlchemy 0.4-Reihe auf 0.5 migrieren. Sie wird auch für diejenigen empfohlen, die mit Essential SQLAlchemy arbeiten, das nur 0.4 abdeckt und sogar einige alte 0.3-Eigenheiten enthält. Beachten Sie, dass SQLAlchemy 0.5 viele Verhaltensweisen entfernt, die während der 0.4-Reihe veraltet waren, und auch weitere, spezifische Verhaltensweisen für 0.4 veraltet.

Wichtige Dokumentationsänderungen

Einige Abschnitte der Dokumentation wurden komplett neu geschrieben und können als Einführung in neue ORM-Funktionen dienen. Insbesondere die Objekte Query und Session weisen deutliche Unterschiede in API und Verhalten auf, die viele der grundlegenden Arbeitsweisen grundlegend verändern, insbesondere im Hinblick auf die Erstellung hochgradig angepasster ORM-Abfragen und den Umgang mit veralteten Sitzungszuständen, Commits und Rollbacks.

Deprecations Quelle

Eine weitere Informationsquelle ist in einer Reihe von Unit-Tests dokumentiert, die den aktuellen Stand der Nutzung einiger gängiger Query-Muster veranschaulichen; diese Datei kann unter [source:sqlalchemy/trunk/test/orm/test_deprecations.py] eingesehen werden.

Änderungen bei den Anforderungen

  • Python 2.4 oder höher ist erforderlich. Die SQLAlchemy 0.4-Linie ist die letzte Version mit Python 2.3-Unterstützung.

Objektrelationale Abbildung

  • Spaltenbasierte Ausdrücke innerhalb von Query. - Wie im Tutorial detailliert, hat Query die Fähigkeit, spezifische SELECT-Anweisungen zu erstellen, nicht nur für vollständige Zeilen.

    session.query(User.name, func.count(Address.id).label("numaddresses")).join(
        Address
    ).group_by(User.name)

    Die Tupel, die von Mehrspalten-/Entitätsabfragen zurückgegeben werden, sind benannte Tupel.

    for row in (
        session.query(User.name, func.count(Address.id).label("numaddresses"))
        .join(Address)
        .group_by(User.name)
    ):
        print("name", row.name, "number", row.numaddresses)

    Query verfügt über einen statement-Accessor sowie eine subquery()-Methode, die es ermöglichen, Query zur Erstellung komplexerer Kombinationen zu verwenden.

    subq = (
        session.query(Keyword.id.label("keyword_id"))
        .filter(Keyword.name.in_(["beans", "carrots"]))
        .subquery()
    )
    recipes = session.query(Recipe).filter(
        exists()
        .where(Recipe.id == recipe_keywords.c.recipe_id)
        .where(recipe_keywords.c.keyword_id == subq.c.keyword_id)
    )
  • Explizite ORM-Aliase werden für Aliase-Joins empfohlen - Die Funktion aliased() erzeugt einen „Alias“ einer Klasse, der eine feingranulare Kontrolle über Aliase in Verbindung mit ORM-Abfragen ermöglicht. Während ein Tabellen-Alias (d. h. table.alias()) immer noch verwendbar ist, behält ein ORM-Alias die Semantik des ORM-gemappten Objekts bei, was für Vererbungszuordnungen, Optionen und andere Szenarien bedeutsam ist. Z.B.

    Friend = aliased(Person)
    session.query(Person, Friend).join((Friend, Person.friends)).all()
  • query.join() stark verbessert. - Sie können jetzt das Ziel und die ON-Klausel für einen Join auf verschiedene Arten angeben. Eine Zielklasse allein kann angegeben werden, wobei SQLA versucht, einen Join zu ihr über einen Fremdschlüssel herzustellen, ähnlich wie bei table.join(someothertable). Ein Ziel und eine explizite ON-Bedingung können angegeben werden, wobei die ON-Bedingung ein relation()-Name, ein tatsächlicher Klassen-Deskriptor oder ein SQL-Ausdruck sein kann. Oder die alte Methode, nur einen relation()-Namen oder Klassen-Deskriptor anzugeben, funktioniert auch. Siehe das ORM-Tutorial, das mehrere Beispiele enthält.

  • Deklarative wird für Anwendungen empfohlen, die keine Abstraktion zwischen Tabellen und Mappern erfordern (und nicht bevorzugen) - Das [/docs/05/reference/ext/declarative.html Deklarative]-Modul, das zur Kombination des Ausdrucks von Table, mapper() und benutzerdefinierten Klassenobjekten verwendet wird, wird dringend empfohlen, da es die Anwendungskonfiguration vereinfacht, das „ein Mapper pro Klasse“-Muster sicherstellt und den vollen Konfigurationsumfang ermöglicht, der für separate mapper()-Aufrufe verfügbar ist. Die separate Verwendung von mapper() und Table wird nun als „klassische SQLAlchemy-Nutzung“ bezeichnet und ist natürlich frei mit Deklarative mischebar.

  • Das .c.-Attribut wurde aus Klassen entfernt (d. h. MyClass.c.somecolumn). Wie in 0.4 sind klassenbasierte Eigenschaften als Abfrageelemente nutzbar, d. h. Class.c.propname wird nun von Class.propname abgelöst, und das c-Attribut verbleibt weiterhin auf Table-Objekten, wo es den Namensraum der auf der Tabelle vorhandenen Column-Objekte angibt.

    Um auf die Tabelle für eine gemappte Klasse zuzugreifen (falls Sie diese nicht bereits aufbewahrt haben)

    table = class_mapper(someclass).mapped_table

    Spalten durchlaufen

    for col in table.c:
        print(col)

    Mit einer bestimmten Spalte arbeiten

    table.c.somecolumn

    Die klassengebundenen Deskriptoren unterstützen die vollständige Menge an Spaltenoperatoren sowie die dokumentierten beziehungsbezogenen Operatoren wie has(), any(), contains() usw.

    Der Grund für die harte Entfernung von .c. ist, dass in 0.5 klassengebundene Deskriptoren potenziell unterschiedliche Bedeutungen und Informationen bezüglich Klassenabbildungen im Vergleich zu einfachen Column-Objekten tragen – und es gibt Anwendungsfälle, bei denen man spezifisch das eine oder das andere verwenden möchte. Im Allgemeinen ruft die Verwendung klassengebundener Deskriptoren eine Reihe von Mapping-/polymorphiebewussten Übersetzungen auf, und die Verwendung tabellenbasierter Spalten nicht. In 0.4 wurden diese Übersetzungen pauschal auf alle Ausdrücke angewendet, 0.5 unterscheidet jedoch vollständig zwischen Spalten und gemappten Deskriptoren und wendet Übersetzungen nur auf letztere an. In vielen Fällen, insbesondere bei Joined-Table-Inheritance-Konfigurationen sowie bei der Verwendung von query(<columns>), sind Class.propname und table.c.colname nicht austauschbar.

    Zum Beispiel unterscheidet sich session.query(users.c.id, users.c.name) von session.query(User.id, User.name); im letzteren Fall ist die Query sich des verwendeten Mappers bewusst und weitere mappertypspezifische Operationen wie query.join(<propname>), query.with_parent() usw. können verwendet werden, im ersteren Fall jedoch nicht. Darüber hinaus beziehen sich in polymorphen Vererbungsszenarien die klassengebundenen Deskriptoren auf die Spalten im verwendeten polymorphen wählbaren Objekt und nicht notwendigerweise auf die Tabellenspalte, die direkt dem Deskriptor entspricht. Zum Beispiel werden eine Reihe von Klassen, die durch Joined-Table-Inheritance mit der person-Tabelle über die person_id-Spalte jeder Tabelle verbunden sind, alle ihr Class.person_id-Attribut auf die person_id-Spalte in person und nicht auf ihre Unterklassentabelle gemappt haben. Version 0.4 würde dieses Verhalten automatisch auf tabellenbasierte Column-Objekte anwenden. In 0.5 wurde diese automatische Konvertierung entfernt, sodass Sie tatsächlich tabellenbasierte Spalten als Mittel zur Überschreibung der Übersetzungen verwenden *können*, die bei polymorphen Abfragen auftreten; dies ermöglicht Query, optimierte Selects für Joined-Table- oder Concrete-Table-Inheritance-Setups sowie portierbare Subqueries usw. zu erstellen.

  • Sitzung synchronisiert sich jetzt automatisch mit Transaktionen. Die Sitzung synchronisiert sich jetzt standardmäßig automatisch mit der Transaktion, einschließlich Autoflush und Autoexpire. Eine Transaktion ist jederzeit vorhanden, es sei denn, sie wird über die Option autocommit deaktiviert. Wenn alle drei Flags auf ihre Standardwerte gesetzt sind, erholt sich die Sitzung nach Rollbacks gut und es ist sehr schwierig, veraltete Daten in die Sitzung zu bekommen. Siehe die neue Sitzungsdokumentation für Details.

  • Implizites Order By wurde entfernt. Dies wirkt sich auf ORM-Benutzer aus, die sich auf das „implizite Ordering“-Verhalten von SA verlassen, das besagt, dass alle Query-Objekte, die keine order_by() haben, nach der „id“- oder „oid“-Spalte der primär gemappten Tabelle sortiert werden und alle lazy/eagerly geladenen Sammlungen eine ähnliche Sortierung anwenden. In 0.5 muss die automatische Sortierung explizit auf mapper() und relation()-Objekten (falls gewünscht) oder ansonsten bei Verwendung von Query konfiguriert werden.

    Um ein 0.4-Mapping auf 0.5 zu konvertieren, damit sein Sortierverhalten dem von 0.4 oder früher extrem ähnlich ist, verwenden Sie die Einstellung order_by auf mapper() und relation()

    mapper(
        User,
        users,
        properties={"addresses": relation(Address, order_by=addresses.c.id)},
        order_by=users.c.id,
    )

    Um die Sortierung für einen Backref festzulegen, verwenden Sie die Funktion backref()

    "keywords": relation(
        Keyword,
        secondary=item_keywords,
        order_by=keywords.c.name,
        backref=backref("items", order_by=items.c.id),
    )

    Deklarativ verwenden? Um die neue order_by-Anforderung zu unterstützen, können order_by und ähnliche nun mit Zeichenfolgen gesetzt werden, die später in Python ausgewertet werden (dies funktioniert **nur** mit Deklarative, nicht mit reinen Mappern)

    class MyClass(MyDeclarativeBase):
        ...
        "addresses": relation("Address", order_by="Address.id")

    Es ist im Allgemeinen eine gute Idee, order_by auf relation()s zu setzen, die Listen-basierte Sammlungen von Elementen laden, da diese Sortierung sonst nicht beeinflusst werden kann. Abgesehen davon ist die beste Praxis, Query.order_by() zu verwenden, um die Sortierung der primär geladenen Entitäten zu steuern.

  • Session ist jetzt autoflush=True/autoexpire=True/autocommit=False. - Um sie einzurichten, rufen Sie einfach sessionmaker() ohne Argumente auf. Der Name transactional=True ist jetzt autocommit=False. Flushes erfolgen bei jeder Abfrage (deaktivierbar mit autoflush=False), bei jedem commit() (wie immer) und vor jedem begin_nested() (damit das Zurückrollen zum SAVEPOINT sinnvoll ist). Alle Objekte werden nach jedem commit() und nach jedem rollback() abgelaufen. Nach dem Rollback werden ausstehende Objekte gelöscht, gelöschte Objekte werden wieder in den persistenten Zustand versetzt. Diese Standardeinstellungen funktionieren sehr gut zusammen und es besteht wirklich kein Bedarf mehr an alten Techniken wie clear() (das in expunge_all() umbenannt wurde).

    PS: Sitzungen sind nach einem rollback() wieder wiederverwendbar. Skalare und Sammlungsattributänderungen, Hinzufügungen und Löschungen werden alle zurückgerollt.

  • session.add() ersetzt session.save(), session.update(), session.save_or_update(). - Die Methoden session.add(someitem) und session.add_all([list of items]) ersetzen save(), update() und save_or_update(). Diese Methoden bleiben während 0.5 veraltet.

  • Backref-Konfiguration weniger ausführlich. - Die Funktion backref() verwendet jetzt die Argumente primaryjoin und secondaryjoin der nach vorne gerichteten relation(), wenn diese nicht explizit angegeben sind. Es ist nicht mehr notwendig, primaryjoin/secondaryjoin in beide Richtungen separat anzugeben.

  • Vereinfachte polymorphe Optionen. - Das „polymorphic load“-Verhalten des ORM wurde vereinfacht. In 0.4 hatte mapper() ein Argument namens polymorphic_fetch, das als select oder deferred konfiguriert werden konnte. Diese Option wurde entfernt; der Mapper wird nun einfach alle Spalten verzögern, die nicht in der SELECT-Anweisung vorhanden waren. Die tatsächliche SELECT-Anweisung wird durch das Mapper-Argument with_polymorphic (das auch in 0.4 vorhanden ist und select_table ersetzt) sowie durch die Methode with_polymorphic() auf Query (ebenfalls in 0.4) gesteuert.

    Eine Verbesserung der verzögerten Ladung von erbenden Klassen besteht darin, dass der Mapper nun in allen Fällen die „optimierte“ Version der SELECT-Anweisung erzeugt; das heißt, wenn Klasse B von A erbt und mehrere Attribute, die nur auf Klasse B vorhanden sind, abgelaufen sind, enthält die Aktualisierungsoperation nur die Tabelle von B in der SELECT-Anweisung und wird nicht zu A verknüpft.

  • Die Methode execute() auf Session konvertiert einfache Zeichenfolgen in text()-Konstrukte, sodass Bindungsparameter alle als „:bindname“ angegeben werden können, ohne text() explizit aufrufen zu müssen. Wenn hier „roher“ SQL gewünscht ist, verwenden Sie session.connection().execute("raw text").

  • session.Query().iterate_instances() wurde in instances() umbenannt. Die alte Methode instances(), die eine Liste statt eines Iterators zurückgab, existiert nicht mehr. Wenn Sie sich auf dieses Verhalten verlassen haben, sollten Sie list(your_query.instances()) verwenden.

Erweiterung des ORM

In 0.5 gehen wir mit mehr Möglichkeiten zur Modifizierung und Erweiterung des ORM voran. Hier ist eine Zusammenfassung

  • MapperExtension. - Dies ist die klassische Erweiterungsklasse, die erhalten bleibt. Methoden, die selten benötigt werden sollten, sind create_instance() und populate_instance(). Um die Initialisierung eines Objekts zu steuern, wenn es aus der Datenbank geladen wird, verwenden Sie die Methode reconstruct_instance() oder einfacher den Dekorator @reconstructor, der in der Dokumentation beschrieben ist.

  • SessionExtension. - Dies ist eine einfach zu verwendende Erweiterungsklasse für Sitzungsereignisse. Insbesondere bietet sie die Methoden before_flush(), after_flush() und after_flush_postexec(). Diese Verwendung wird in vielen Fällen gegenüber MapperExtension.before_XXX empfohlen, da Sie innerhalb von before_flush() den Flush-Plan der Sitzung frei ändern können, was aus MapperExtension nicht möglich ist.

  • AttributeExtension. - Diese Klasse ist jetzt Teil der öffentlichen API und ermöglicht die Abfangen von Userland-Ereignissen auf Attributen, einschließlich Attributset- und Löschoperationen sowie Sammlungsanhänge und -entfernungen. Sie ermöglicht auch die Modifizierung des gesetzten oder angehängten Wertes. Der Dekorator @validates, der in der Dokumentation beschrieben ist, bietet eine schnelle Möglichkeit, alle gemappten Attribute als durch eine bestimmte Klassenmethode „validiert“ zu markieren.

  • Anpassung der Attributinstrumentierung. - Eine API wird für ehrgeizige Bemühungen bereitgestellt, die Attributinstrumentierung von SQLAlchemy vollständig zu ersetzen oder sie in einigen Fällen einfach zu ergänzen. Diese API wurde für das Trellis-Toolkit erstellt, ist aber als öffentliche API verfügbar. Einige Beispiele sind in der Distribution im Verzeichnis /examples/custom_attributes enthalten.

Schema/Typen

  • String ohne Länge generiert nicht mehr TEXT, sondern VARCHAR - Der Typ String wird nicht mehr magisch in einen Text-Typ umgewandelt, wenn er ohne Länge angegeben wird. Dies hat nur Auswirkungen, wenn CREATE TABLE ausgegeben wird, da dann VARCHAR ohne Längenparameter ausgegeben wird, was auf vielen (aber nicht allen) Datenbanken nicht gültig ist. Um eine TEXT-Spalte (oder CLOB, d. h. unbegrenzten String) zu erstellen, verwenden Sie den Typ Text.

  • PickleType() mit mutable=True erfordert eine __eq__() Methode - Der Typ PickleType muss Werte vergleichen, wenn mutable=True ist. Die Methode zum Vergleichen von pickle.dumps() ist ineffizient und unzuverlässig. Wenn ein eingehendes Objekt keine __eq__()-Methode implementiert und auch nicht None ist, wird der dumps()-Vergleich verwendet, aber eine Warnung ausgegeben. Für Typen, die __eq__() implementieren (was für alle Dictionaries, Listen usw. gilt), wird der Vergleich mit == durchgeführt und ist jetzt standardmäßig zuverlässig.

  • convert_bind_param() und convert_result_value() Methoden von TypeEngine/TypeDecorator werden entfernt. - Das O’Reilly-Buch hat diese Methoden leider dokumentiert, obwohl sie nach 0.3 als veraltet markiert waren. Für einen benutzerdefinierten Typ, der von TypeEngine erbt, sollten die Methoden bind_processor() und result_processor() für Bindungs-/Ergebnisverarbeitung verwendet werden. Jeder benutzerdefinierte Typ, ob er von TypeEngine oder TypeDecorator erbt und den alten 0.3-Stil verwendet, kann mit dem folgenden Adapter einfach an den neuen Stil angepasst werden

    class AdaptOldConvertMethods(object):
        """A mixin which adapts 0.3-style convert_bind_param and
        convert_result_value methods
    
        """
    
        def bind_processor(self, dialect):
            def convert(value):
                return self.convert_bind_param(value, dialect)
    
            return convert
    
        def result_processor(self, dialect):
            def convert(value):
                return self.convert_result_value(value, dialect)
    
            return convert
    
        def convert_result_value(self, value, dialect):
            return value
    
        def convert_bind_param(self, value, dialect):
            return value

    Um den obigen Mixin zu verwenden

    class MyType(AdaptOldConvertMethods, TypeEngine): ...
  • Das quote-Flag bei Column und Table sowie das quote_schema-Flag bei Table steuern nun die Anführungszeichen sowohl positiv als auch negativ. Der Standardwert ist None, d. h. es werden die regulären Anführungsregeln angewendet. Wenn True, werden Anführungszeichen erzwungen. Wenn False, werden Anführungszeichen deaktiviert.

  • Der DEFAULT-Wert DDL für Spalten kann jetzt bequemer mit Column(..., server_default='val') angegeben werden, was Column(..., PassiveDefault('val')) veraltet. default= ist jetzt ausschließlich für Python-initiierte Standardwerte und kann mit server_default koexistieren. Ein neues server_default=FetchedValue() ersetzt das PassiveDefault('')-Idiom, um Spalten als von externen Triggern beeinflusst zu markieren und hat keine DDL-Nebeneffekte.

  • Die DateTime-, Time- und Date-Typen von SQLite **akzeptieren jetzt nur noch datetime-Objekte, keine Zeichenfolgen** mehr als Eingabe für Bindungsparameter. Wenn Sie Ihren eigenen „hybriden“ Typ erstellen möchten, der Zeichenfolgen akzeptiert und Ergebnisse als Datumsobjekte (aus welchem Format auch immer Sie möchten) zurückgibt, erstellen Sie einen TypeDecorator, der auf String aufbaut. Wenn Sie nur zeichenfolgenbasierte Daten möchten, verwenden Sie einfach String.

  • Zusätzlich stellen die Typen DateTime und Time bei Verwendung mit SQLite nun das „Mikrosekunden“-Feld des Python datetime.datetime-Objekts auf die gleiche Weise wie str(datetime) dar – als Bruchteil von Sekunden, nicht als Anzahl von Mikrosekunden. Das heißt:

    dt = datetime.datetime(2008, 6, 27, 12, 0, 0, 125)  # 125 usec
    
    # old way
    "2008-06-27 12:00:00.125"
    
    # new way
    "2008-06-27 12:00:00.000125"

    Wenn also eine bestehende SQLite-Datenbank, die auf Dateien basiert, über 0.4 und 0.5 hinweg verwendet werden soll, müssen Sie entweder die datetime-Spalten aktualisieren, um das neue Format zu speichern (HINWEIS: Bitte testen Sie dies, ich bin ziemlich sicher, dass es korrekt ist)

    UPDATE mytable SET somedatecol =
      substr(somedatecol, 0, 19) || '.' || substr((substr(somedatecol, 21, -1) / 1000000), 3, -1);

    oder den „Legacy“-Modus wie folgt aktivieren

    from sqlalchemy.databases.sqlite import DateTimeMixin
    
    DateTimeMixin.__legacy_microseconds__ = True

Connection Pool nicht mehr standardmäßig threadlocal

0.4 hat eine unglückliche Standardeinstellung von „pool_threadlocal=True“, die zu überraschendem Verhalten führt, wenn beispielsweise mehrere Sitzungen innerhalb eines Threads verwendet werden. Dieses Flag ist in 0.5 jetzt deaktiviert. Um das Verhalten von 0.4 wieder zu aktivieren, geben Sie pool_threadlocal=True an create_engine() an oder verwenden Sie alternativ die Strategie „threadlocal“ über strategy="threadlocal".

*args akzeptiert, *args nicht mehr akzeptiert

Die Richtlinie für method(\*args) vs. method([args]) lautet: Wenn die Methode eine variable Anzahl von Elementen akzeptiert, die eine feste Struktur darstellen, nimmt sie \*args. Wenn die Methode eine variable Anzahl von Elementen akzeptiert, die datengesteuert sind, nimmt sie [args].

  • Die verschiedenen Funktionen eagerload(), eagerload_all(), lazyload(), contains_eager(), defer(), undefer() von Query.options() akzeptieren jetzt variable Länge von \*keys als ihr Argument, was einen Pfad ermöglicht, der über Deskriptoren formuliert wird, z. B.:

    query.options(eagerload_all(User.orders, Order.items, Item.keywords))

    Ein einzelnes Array-Argument wird aus Kompatibilitätsgründen immer noch akzeptiert.

  • Ähnlich akzeptieren die Methoden Query.join() und Query.outerjoin() eine variable Länge von *args, wobei ein einzelnes Array zur Rückwärtskompatibilität akzeptiert wird.

    query.join("orders", "items")
    query.join(User.orders, Order.items)
  • Die Methode in_() auf Spalten und ähnlichem akzeptiert jetzt nur noch ein Listenargument. Sie akzeptiert keine \*args mehr.

Entfernt

  • entity_name - Dieses Feature war immer problematisch und selten genutzt. Die detaillierteren Anwendungsfälle von 0.5 offenbarten weitere Probleme mit entity_name, die zu seiner Entfernung führten. Wenn für eine einzelne Klasse unterschiedliche Mappings erforderlich sind, teilen Sie die Klasse in separate Unterklassen auf und mappen Sie sie separat. Ein Beispiel hierfür finden Sie unter [wiki:UsageRecipes/EntityName]. Weitere Informationen zur Begründung finden Sie unter https://groups.google.c om/group/sqlalchemy/browse_thread/thread/9e23a0641a88b96d?hl=en.

  • get()/load() Bereinigung

    Die Methode load() wurde entfernt. Ihre Funktionalität war eher willkürlich und kopierte im Wesentlichen Hibernate, wo sie ebenfalls keine besonders sinnvolle Methode ist.

    Um eine äquivalente Funktionalität zu erhalten

    x = session.query(SomeClass).populate_existing().get(7)

    Session.get(cls, id) und Session.load(cls, id) wurden entfernt. Session.get() ist redundant gegenüber session.query(cls).get(id).

    MapperExtension.get() ist ebenfalls entfernt (wie auch MapperExtension.load()). Um die Funktionalität von Query.get() zu überschreiben, verwenden Sie eine Unterklasse

    class MyQuery(Query):
        def get(self, ident): ...
    
    
    session = sessionmaker(query_cls=MyQuery)()
    
    ad1 = session.query(Address).get(1)
  • sqlalchemy.orm.relation()

    Die folgenden veralteten Schlüsselwortargumente wurden entfernt

    foreignkey, association, private, attributeext, is_backref

    Insbesondere wird attributeext durch extension ersetzt – die Klasse AttributeExtension ist jetzt Teil der öffentlichen API.

  • session.Query()

    Die folgenden veralteten Funktionen wurden entfernt

    list, scalar, count_by, select_whereclause, get_by, select_by, join_by, selectfirst, selectone, select, execute, select_statement, select_text, join_to, join_via, selectfirst_by, selectone_by, apply_max, apply_min, apply_avg, apply_sum

    Zusätzlich wurde das Schlüsselwortargument id für join(), outerjoin(), add_entity() und add_column() entfernt. Um Tabellenaliase in Query auf Ergebnisspalten zu zielen, verwenden Sie den aliased-Konstrukt.

    from sqlalchemy.orm import aliased
    
    address_alias = aliased(Address)
    print(session.query(User, address_alias).join((address_alias, User.addresses)).all())
  • sqlalchemy.orm.Mapper

    • instances()

    • get_session() - Diese Methode war nicht sehr auffällig, hatte aber den Effekt, dass sie Lazy Loads mit einer bestimmten Sitzung assoziierte, selbst wenn das übergeordnete Objekt vollständig getrennt war, wenn eine Erweiterung wie scoped_session() oder das alte SessionContextExt verwendet wurde. Es ist möglich, dass einige Anwendungen, die sich auf dieses Verhalten verlassen haben, nicht mehr wie erwartet funktionieren; aber die bessere Programmierpraxis ist hier, immer sicherzustellen, dass Objekte innerhalb von Sitzungen vorhanden sind, wenn Datenbankzugriffe von ihren Attributen benötigt werden.

  • mapper(MyClass, mytable)

    Gemappte Klassen werden nicht mehr mit einem „c“-Klassenattribut instrumentiert; z. B. MyClass.c

  • sqlalchemy.orm.collections

    Der Alias _prepare_instrumentation für prepare_instrumentation wurde entfernt.

  • sqlalchemy.orm

    Entfernte den Alias EXT_PASS von EXT_CONTINUE.

  • sqlalchemy.engine

    Der Alias von DefaultDialect.preexecute_sequences zu .preexecute_pk_sequences wurde entfernt.

    Die veraltete Funktion engine_descriptors() wurde entfernt.

  • sqlalchemy.ext.activemapper

    Modul entfernt.

  • sqlalchemy.ext.assignmapper

    Modul entfernt.

  • sqlalchemy.ext.associationproxy

    Das Durchreichen von Keyword-Args bei .append(item, \**kw) des Proxys wurde entfernt und ist jetzt einfach .append(item)

  • sqlalchemy.ext.selectresults, sqlalchemy.mods.selectresults

    Module entfernt.

  • sqlalchemy.ext.declarative

    declared_synonym() entfernt.

  • sqlalchemy.ext.sessioncontext

    Modul entfernt.

  • sqlalchemy.log

    Der Alias SADeprecationWarning für sqlalchemy.exc.SADeprecationWarning wurde entfernt.

  • sqlalchemy.exc

    exc.AssertionError wurde entfernt und die Verwendung wurde durch das gleichnamige Python-Built-in ersetzt.

  • sqlalchemy.databases.mysql

    Die veraltete Dialektmethode get_version_info wurde entfernt.

Umbenannt oder verschoben

  • sqlalchemy.exceptions ist jetzt sqlalchemy.exc

    Das Modul kann bis Version 0.6 noch unter dem alten Namen importiert werden.

  • FlushError, ConcurrentModificationError, UnmappedColumnError -> sqlalchemy.orm.exc

    Diese Ausnahmen wurden in das ORM-Paket verschoben. Der Import von ‚sqlalchemy.orm‘ installiert Aliase in sqlalchemy.exc zur Kompatibilität bis Version 0.6.

  • sqlalchemy.logging -> sqlalchemy.log

    Dieses interne Modul wurde umbenannt. Muss nicht mehr speziell behandelt werden, wenn SA mit py2app und ähnlichen Tools, die Imports scannen, verpackt wird.

  • session.Query().iterate_instances() -> session.Query().instances().

Veraltet

  • Session.save(), Session.update(), Session.save_or_update()

    Alle drei wurden durch Session.add() ersetzt

  • sqlalchemy.PassiveDefault

    Verwenden Sie Column(server_default=...). Übersetzt intern zu sqlalchemy.DefaultClause().

  • session.Query().iterate_instances(). Es wurde in instances() umbenannt.