SQLAlchemy: Ändert der Mapper meine Objekte?

8

Ich versuche, SQLAlchemy Version zu verwenden, um meine Objekte in einer Datenbank zu speichern. Ich habe eine Funktion save(...) für diesen Zweck:

%Vor%

Es scheint mir, dass der Mapper implizit etwas mit meinen Objekten macht, wenn ich den Mapper erstelle und ich sie nicht mehr benutzen kann. Nach dem, was ich in ähnlichen Fragen gelesen habe, ist dies nicht völlig unbekannt.

Wird dieses Verhalten erwartet? Bekomme ich hier etwas Grundlegendes falsch?
Was ist der richtige Weg, um meine Objekte abzubilden und zu speichern?

Zusätzliche Informationen: Ich benutze SQLAlchemy 0.7.5 mit System-Standard Python 2.7.1 auf Mac OSX 10.7.3 Lion und SQLAlchemy 0.6.8 mit System-Standard Python 2.7.2+ auf einer Kubuntu 11.10 virtuellen Maschine / p>

Aktualisierung: Es scheint, dass der SQLAlchemy-Mapper bekannt ist, um Objekte zu ändern in 'SQLAlchemy needs'. Die Lösung in dem verknüpften Artikel ist das Erstellen von Objekten, nachdem mapper(...) aufgerufen wurde.
Ich habe bereits gültige Objekte - aber ich kann sie nicht mehr verwenden ...

Wie bekomme ich SQLAlchemy, um meine Objekte zu speichern?

Update 2: Ich habe den Eindruck, dass ich etwas Grundlegendes über ORMs falsch verstehe:
Ich dachte, das SQLAlchemy-Mapper-Konzept gibt mir die Möglichkeit, meine Objekte zu definieren und mit ihnen in meiner von jeder Datenbank entkoppelten Anwendung zu arbeiten - und nur wenn ich sie beibehalten will, bringe ich SQLAlchemy ein und lasse es die ganze schwere Arbeit des Mappings tun Klassenobjekt in eine Datenbanktabelle. Außerdem kann ich keinen architektonischen Grund sehen, warum SQLAlchemy irgendwo anders in meinem Code erwähnt werden sollte als in den Persistenzfunktionen save(...) und load(...) .

Aus dem Blick auf die erste Antwort unten verstehe ich, dass ich die Klasse der Datenbanktabelle zuordnen muss, bevor ich irgendeines meiner Objekte verwende. Das wäre ganz am Anfang meines Programms. Vielleicht bekomme ich hier etwas falsch, aber das scheint eine ziemlich drastische Designeinschränkung von dem Teil von SQLAlchemy zu sein - die ganze lose Kopplung ist weg. In diesem Sinne sehe ich auch nicht den Vorteil, überhaupt einen Mapper zu haben, da die "späte Kopplung", die ich will, mit SQLAlchemy technisch nicht möglich scheint, und ich könnte diesen "deklarativen Stil" auch einfach machen und mischen vermischen Geschäftslogik mit Persistenz-Code: - (

Update 3: Ich ging zurück zu Platz eins und las SQLAlchemy erneut. Es steht direkt auf der Titelseite :

  

SQLAlchemy ist vor allem für seinen objektrelationalen Mapper (ORM) bekannt, eine optionale Komponente, die das Data-Mapper-Muster bietet, bei dem Klassen in offener Weise auf mehrere Arten auf die Datenbank abgebildet werden können - was dem Objektmodell und dem Datenbankschema erlaubt entwickeln sich von Anfang an sauber entkoppelt.

Nach meinen bisherigen Erfahrungen scheint SQLAlchemy dieses Versprechen jedoch nicht zu erfüllen, da es mich zwingt, Objektmodell und Datenbankschema von Anfang an zu koppeln. Nach allem, was ich über SQLAlchemy gehört habe, fällt es mir wirklich schwer zu glauben, dass dies wirklich der Fall ist, und ich würde eher annehmen, dass ich noch nicht etwas Grundlegendes verstanden habe Was mache ich falsch?

Update 4:

Der Vollständigkeit halber möchte ich den funktionierenden Beispielcode hinzufügen, der alle Zuordnungen vor dem Erstellen von Geschäftsobjekten durchführt:

%Vor%

In meinem eigenen (Nicht-Beispiel-) Code importiere ich MyClass von seinem eigenen Modul und führe das Mapping in einer separaten 'Objekt-Repository'-Klasse durch, die MetaData in ihrer __init__(...) -Methode erzeugt und sie in einem speichert member, so dass die Methoden save(...) und load(...) persistency bei Bedarf darauf zugreifen können. Die Hauptanwendung besteht darin, das Repository-Objekt ganz am Anfang zu erstellen. Dies enthält die Auswirkungen von SQLAlchemy auf das Design einer Klasse, verteilt jedoch auch die Definitionen von Geschäftsobjekten und zugeordneten Tabellen an separate Speicherorte im Code. Ich bin mir noch nicht sicher, ob ich auf lange Sicht gehen werde, aber es scheint für den Moment zu funktionieren.

Letzter Tipp für SQLAlchemy noobs wie mich selbst: Sie müssen durchgängig mit ein und demselben Metadatenobjekt arbeiten, sonst erhalten Sie Ausnahmen wie no such table oder class not mapped .

    
ssc 02.03.2012, 14:34
quelle

2 Antworten

2

SQLAlchemy ändert die zugeordnete Klasse absolut. SQLAlchemy ruft diese Instrumentierung auf.

Beachten Sie:

%Vor%

Ebenso besteht ein Unterschied zwischen einfachen MyClass Instanzen und instrumentierten MyClass Instanzen:

%Vor%

Ihr NoneType -Fehler ist darauf zurückzuführen, dass die jetzt instrumentierte MyClass.title (die durch einen Deskriptor InstrumentedAttribute ersetzt wurde) versucht, die Eigenschaft _sa_instance_state über instance_state(instance) zu erhalten. _sa_instance_state wird von der instrumentierten MyClass bei der Objekterstellung erstellt, nicht jedoch von der nicht instrumentierten MyClass .

Instrumentation ist wichtig. Dies geschieht, damit der Attributzugriff, die Zuweisung und andere wichtige Zustandsänderungen des Objekts dem Abbilder unter Verwendung von Deskriptoren mitgeteilt werden können. (Wie wäre zum Beispiel das verzögerte Laden von Spalten oder sogar der Zugriff auf Sammlungen möglich, ohne dass die Klasse die Attributzugriffe im Objekt überwacht?)% Co_de% -Objekte sind nicht an die -Tabelle gebunden, müssen sie aber an den Mapper gebunden sein. Sie können die Zuordnungsoptionen und die Tabelle ändern, ohne den code Ihrer Domänenobjekte zu ändern, aber nicht unbedingt Ihre Domänenobjekte selbst . (Immerhin können Sie ein einzelnes Objekt mit mehreren Mappern und mehreren Tabellen verbinden.)

Stellen Sie sich Instrumentierung vor, die eine "Subject" -Implementierung einer zugeordneten Klasse für den "Observer" -Mapper in einem Subjekt-Beobachter-Muster transparent hinzufügt. Offensichtlich können Sie kein Subjekt-Beobachter-Muster für ein beliebiges Objekt implementieren - Sie müssen das beobachtete Objekt an die Subjekt-Schnittstelle anpassen.

Ich nehme an, es ist vielleicht möglich, eine Instanz an Ort und Stelle zu instrumentieren, indem ein MyClass -Objekt dafür erstellt wird (ich weiß nicht wie - ich müsste den _sa_instance_state Code lesen), aber ich tue nicht t sehen, warum das notwendig ist. Wenn die Möglichkeit besteht, dass Ihr Objekt von SQLAlchemy beibehalten wird, definieren Sie einfach das Mapping und die Tabelle für diese Persistenz, bevor Sie Instanzen des Objekts erstellen. Sie müssen keine Engine oder Sitzung erstellen oder überhaupt eine Datenbank haben, um das Mapping zu definieren.

Der einzige Weg, wie Sie mit völlig uninstrumentierten Anwendungsobjekten durchkommen können, ist, wenn Ihr Anwendungsfall extrem trivial ist, etwas wie das Äquivalent von mapper() oder pickle von dicts. ZB ohne Instrumentierung können Sie verwandte Objekte niemals in Sammlungsattributen laden oder speichern. Hast du wirklich nie vor, dies zu tun? Wenn dies der Fall ist, werden Sie vielleicht glücklicher sein, wenn Sie unpickle überhaupt nicht verwenden und einfach den zugrunde liegenden Ausdruck verwenden API , um Ihre Instanzen zu erhalten?

    
Francis Avila 03.03.2012, 07:51
quelle
0

Sie müssen die Zuordnung vor dem Erstellen von MyClass-Objekten entweder am Anfang des 'if' oder davor vornehmen.

Gibt es einen bestimmten Grund, warum Sie die Instanzen erstellen müssen, bevor Sie selbst entscheiden, zu welcher Tabelle sie gehören?

    
Marco Mariani 02.03.2012 14:47
quelle

Tags und Links