Skip to content

Storage layer#

Die Storage-Schicht in RESTAlchemy ist für das Persistieren von DM-Modellen und deren Wiederherstellung zuständig.

Sie bildet eine eigene Schicht zwischen Data Model (DM) und API.


Modulüberblick#

Wichtige Module für SQL-Storage:

  • restalchemy.storage.base
  • Abstrakte Interfaces für speicherbare Modelle und Collections.
  • restalchemy.storage.exceptions
  • Storage-spezifische Exceptions.
  • restalchemy.storage.sql.engines
  • Engine-Factory und Implementierungen für MySQL/PostgreSQL.
  • restalchemy.storage.sql.sessions
  • Datenbank-Sessions, Transaktionen, Session-Query-Cache.
  • restalchemy.storage.sql.orm
  • ORM-ähnliche Mixins und Collections (SQLStorableMixin, ObjectCollection).
  • restalchemy.storage.sql.tables
  • Tabellenabstraktion für ORM und Dialekte.
  • restalchemy.storage.sql.dialect.*
  • Dialekt-spezifische Query-Builder für MySQL und PostgreSQL.

Im Normalfall arbeiten Sie nur mit:

  • DM-Modellen + orm.SQLStorableMixin;
  • engines.engine_factory.configure_factory() zur Konfiguration des Engines;
  • Model.objects und den Methoden save() / delete().

Architekturüberblick#

1. DM-Modell#

Sie definieren ein Modell, das von:

  • models.ModelWithUUID (oder einer anderen Model*-Basisklasse) und
  • orm.SQLStorableMixin

erbt.

Beispiel (vereinfacht aus examples/dm_mysql_storage.py):

from restalchemy.dm import models, properties, relationships, types
from restalchemy.storage.sql import orm


class FooModel(models.ModelWithUUID, orm.SQLStorableMixin):
    __tablename__ = "foos"
    foo_field1 = properties.property(types.Integer(), required=True)
    foo_field2 = properties.property(types.String(), default="foo_str")


class BarModel(models.ModelWithUUID, orm.SQLStorableMixin):
    __tablename__ = "bars"
    bar_field1 = properties.property(types.String(min_length=1, max_length=10))
    foo = relationships.relationship(FooModel)

2. Engine und Sessions#

restalchemy.storage.sql.engines enthält eine engine_factory, die SQL-Engines verwaltet.

Typische Konfiguration:

from restalchemy.storage.sql import engines

engines.engine_factory.configure_factory(
    db_url="mysql://user:password@127.0.0.1:3306/mydb",
)

Kernideen:

  • Engine parst db_url, konfiguriert Connection-Pool und Dialekt.
  • Session (PgSQLSession / MySQLSession) führt Statements aus.
  • session_manager(engine, session=None) (in sessions.py) kapselt Workflows in Transaktionen.

3. ORM-Mixins und Collections#

restalchemy.storage.sql.orm stellt bereit:

  • SQLStorableMixin — Mixin für DM-Modelle, die in SQL gespeichert werden.
  • ObjectCollection — Collection-API, erreichbar über Model.objects.

Verantwortlichkeiten:

  • SQLStorableMixin:
  • Verknüpft DM-Modell und Tabelle über __tablename__ und get_table().
  • Implementiert insert(), save(), update(), delete().
  • Konvertiert DM-Properties in speicherbare Werte und zurück.

  • ObjectCollection:

  • Methoden get_all(), get_one(), get_one_or_none(), query(), count().
  • Nutzt restalchemy.dm.filters zur Beschreibung von WHERE-Bedingungen.

4. Dialekte und Tabellen#

Dialekt-Module (restalchemy.storage.sql.dialect.*) und tables.SQLTable:

  • Erzeugen SQL-Statements (SELECT/INSERT/UPDATE/DELETE).
  • Binden Parameter.
  • Führen Queries über Sessions aus.

Diese Komponenten sind intern; Sie müssen sie selten direkt verwenden.

5. Was das Lesen einer Zeile tut#

  • Ein Modell ohne vorgeladene Beziehungen wird aus einer Tabelle gelesen, und seine Spalten werden unter ihren eigenen Namen selektiert. Ein Prefetch fügt einen JOIN hinzu; dann erhält jede Spalte einen Alias, damit zwei Tabellen gleichnamige Spalten selektieren können.
  • Ein gespeichertes NULL ist ein Wert, den niemand angegeben hat: der Default der Property greift, genau wie bei einem im Konstruktor ausgelassenen Wert.
  • Ein Wert, den der Typ selbst aus der gespeicherten Form baut — UUID, Boolean, der von UTCDateTimeZ auf UTC gebrachte Zeitstempel — wird gegen diesen Typ nicht erneut geprüft. Alle anderen Typen werden wie bisher geprüft, und ebenso alles, was einen dieser drei erbt: eine Unterklasse behält die Umwandlung, kann aber eine eigene Regel haben.
  • Jedes Lesen kommt bei restore_row an — ein Modell wie eine ganze Seite —, also gehört dorthin, was ein Modell bei jedem Lesen tun will. restore_from_storage ist ein Weg hinein und kein Weg, das Lesen zu ändern: Es zu überschreiben lässt eine Seite von Zeilen unberührt.

Lebenszyklus eines SQL-gestützten Modells#

  1. Modell definieren
  2. Von ModelWithUUID und SQLStorableMixin erben.
  3. __tablename__ festlegen.
  4. Felder und Beziehungen über DM-Properties definieren.

  5. Engine konfigurieren

  6. Einmalig beim Start: engine_factory.configure_factory(db_url=...).

  7. Tabellen/Migrationen

  8. Mit ra-new-migration und ra-apply-migration Datenbank-Schema anlegen/ändern.

  9. CRUD-Operationen

  10. Instanzen erzeugen und .save() aufrufen.
  11. Über Model.objects.get_all() / .get_one(filters=...) lesen.
  12. .delete() zum Löschen verwenden.

  13. Filter und komplexe Queries

  14. Filter mit restalchemy.dm.filters aufbauen und an objects.get_all() / get_one() übergeben.

  15. Transaktionen und Sessions (optional)

  16. Für feingranulare Kontrolle session_manager() explizit verwenden.

Alle Schritte werden ausführlich im DM+SQL How-to und in examples/dm_mysql_storage.py sowie examples/dm_pg_storage.py gezeigt.