Ich habe vor kurzem begonnen, an Flask und Flask-SQLAlchemy zu arbeiten. Aus dem Hintergrund von Django kommend, fand ich Flask-SQLAlchmey ziemlich komplex. Ich habe gelesen, dass SQLAlchemy Data Mapper-Muster implementiert, während Django ORM auf Active Record Pattern basiert.
Hier ist ein Beispielcode geschrieben, der Repository-Muster implementiert, um auf die Datenbank zuzugreifen.
Hier ist ein weiterer Link zu einem Kommentar von S.Lott (271k Reputation), der sagt, dass ORM die Datenzugriffsebene ist und vom Modell getrennt ist.
Meine Fragen sind diese:
Question.query.filter_by(text = text).all()
nicht besser zu verwenden als
db.session.query(Question).filter(Question.text == text).all()
? Dies ist kein Duplikat von DataMapper vs ActiveRecord Muster weil das nur die Definition sagt, interessiere ich mich mehr für die praktischen Beispiele.
Punkt für Punkt.
Ich habe eine Legacy-Datenbank, für die ich ein paar Dienstprogramme zur Datenverarbeitung schreiben muss. Die Verwendung des Mapper-Musters ohne ORM- / ActiveRecord-Stil machte Dinge für mich beim Schreiben von Abfragen so einfach wie ActiveRecord. Es arbeitet mit netten zusammensetzbaren Objekten, die SQL-Klauseln ähneln und vor SQL-Injektionen abgeschirmt sind.
Objekte, die "passiv" sind, erlauben mehr Flexibilität / Einheitlichkeit: Ein Ergebnis eines komplexen Joins ist ein benanntes Tupel, wie es das Ergebnis einer einfachen Auswahl ist. Es gibt keine Identität, die berücksichtigt werden muss, keine zwischengespeicherten Objekte mit derselben Identität.
Alle Aktualisierungen sind explizit; kein "Speichern" eines Zustands, der anderswo geändert wurde, keine Hooks auf .save()
, etc. Dies machte effiziente Stapelupdates trivial, ohne zu stören, wenn die richtigen Daten an die DB gesendet wurden. In meinem Fall waren beide Vorteile. Im Allgemeinen "hängt es ab". Zum Beispiel musste ich Datenbank-generierte IDs nach Einfügungen manuell abrufen. Diese Abfrage explizit auszuführen ist ein bisschen mehr Arbeit. In der Lage zu sein, dies in einer Abfrage statt in einer pro Datensatz zu tun, war in meinem Fall ein riesiger Segen.
SQLAlchemy verfügt über ein mehrschichtiges Design, mit dem Sie auf die untere Ebene des "Mappers" zugreifen können, auch wenn Sie Dinge auf der oberen ORM-Ebene deklarieren und normalerweise damit arbeiten. In Django zum Beispiel ist es nicht so einfach, wenn / wann immer noch möglich.
Im Beispiel sieht das 'Repository' wie eine Ebene aus, die über dem 'Mapper' erstellt wurde. Das Repository könnte auf einer einfachen DBAPI basieren, aber der Mapper macht ein paar Dinge einfacher, wie eine schönere Parameterbindung, benannte Tupel für die Ergebnismengen und einen Wrapper über normalem SQL mit zusammensetzbaren, wiederverwendbaren Teilen.
Der Mapper bietet auch ein gewisses Maß an Datenbankunabhängigkeit. Z.B. SQL Server und Postgres haben verschiedene Möglichkeiten zum Verketten von Zeichenfolgen. Der Mapper bietet eine einheitliche Schnittstelle.
Sie schreiben Ihre select
, wo Sie sie verwenden. Wenn Sie eine Auswahl haben, die Sie ständig in verschiedenen Kontexten wiederverwenden, können Sie sie in eine Methode oder Funktion einfügen. Die meisten Auswahlen haben eine Verwendung und sind an Ort und Stelle gebaut.
Eine nette Eigenschaft von SQLAlchemy's Design ist, dass Sie einfach Bedingungen und ganze where
-Klauseln speichern und sie über die select / update / delete -Anweisungen wiederverwenden können.
Question.query.filter_by(text = text).all()
verwendet eine implizite Transaktion.
db.session.query(Question).filter(Question.text == text).all()
verwendet eine explizite Transaktion.
Explizite Transaktionen geben Ihnen Sicherheit mit DML. Sie sind auch wichtig mit select
s, wenn Sie eine sich schnell ändernde Datenbank abfragen und möchten, dass Ihre mehreren verwandten select
s denselben konsistenten Zustand sehen.
Normalerweise schreibe ich einen trivialen Wrapper um sessionmaker
und schreibe Dinge wie folgt:
Wenn ich definitiv weiß, dass in diesem Block keine DML laufen soll, verwende ich .readonly_transaction()
, das immer zurückrollt.
In vielen Fällen ist die implizite Transaktion in Ordnung. Django ermöglicht es Ihnen, eine Methode mit @transaction.atomic
zu dekorieren und eine semi-explizite Transaktionssteuerung zu haben, die in 99% der Fälle ausreicht. Aber manchmal braucht man noch feinere Granularität.
Stimmen Sie der obigen Antwort vollkommen zu: Ja, das Data-Mapper-Muster von SQLAlchemy ist wirklich flexibler und für komplexe Abfragen ist es wirklich mächtiger, weniger magisch und kontrollierter.
Aber in einfachen Aufgaben wie CRUD wird SQLAlchemy's Code zu übergewichtig / exzessiv / redundant.
Um zum Beispiel einfach ein Objekt im einfachsten "create" -Controller zu erstellen, benötigen Sie etwas wie folgt:
%Vor%In Active Record ORM benötigen Sie nur eine einzige Zeichenfolge.
Nun, für einfache Aufgaben möchten einige von uns vielleicht etwas einfacheres. Ich meine, es wird cool sein, Active Record für SQLAlchemy zu haben.
Gute Neuigkeiten: Ich habe kürzlich ein Paket dafür erstellt (es enthält auch andere nützliche Dinge).
Schau es dir an: Ссылка
Tags und Links python activerecord design-patterns flask-sqlalchemy datamapper