DM-Modelle#
Modul: restalchemy.dm.models
Dieses Modul definiert Basisklassen und Mixins für Datenmodelle (DM) in RESTAlchemy.
MetaModel und Model#
MetaModel#
MetaModel ist die Metaklasse aller DM-Modelle. Sie:
- Sammelt Felddefinitionen, die mit
properties.property()undproperties.container()erstellt wurden. - Führt Properties aus Basisklassen zusammen.
- Verfolgt ID-Properties in
id_properties. - Hängt pro Modellklasse ein operatives Storage
__operational_storage__an.
Normalerweise benutzen Sie MetaModel nicht direkt, sondern erben von Model oder dessen Subklassen.
Model#
Model ist die grundlegende Basisklasse für DM-Modelle:
from restalchemy.dm import models, properties, types
class Foo(models.Model):
foo_id = properties.property(types.Integer(), id_property=True, required=True)
name = properties.property(types.String(max_length=255), default="")
Wichtiges Verhalten:
- Der Konstruktor nimmt Keyword-Argumente entgegen und gibt sie an
pour()weiter. pour()baut einenPropertyManageraus derproperties-Collection und validiert die Felder.- Attributzugriffe werden auf Properties gemappt:
model.fieldliestproperties[field].value.model.field = valuesetzt den Wert mit Validierung.as_plain_dict()gibt eine einfache Dictionary-Repräsentation zurück.- Das Modell verhält sich wie ein Mapping über seine Properties (
__getitem__,__iter__,__len__).
Fehlerbehandlung:
- Falscher Typ →
ModelTypeError. - Fehlendes Pflichtfeld →
PropertyRequired. - Änderung eines read-only oder ID-Feldes →
ReadOnlyProperty.
Eigene Validierung:
class PositiveFoo(models.Model):
value = properties.property(types.Integer(), required=True)
def validate(self):
if self.value <= 0:
raise ValueError("value must be positive")
validate() wird aus pour() heraus aufgerufen.
ID-Verarbeitung#
ModelWithID#
ModelWithID erweitert Model für Modelle mit genau einem ID-Property:
get_id()gibt den aktuellen Wert des ID-Feldes zurück.- Gleichheit und Hashing basieren auf
get_id().
Wenn es kein oder mehrere ID-Felder gibt, wirft get_id_property() einen TypeError, und Sie sollten die ID-Logik selbst implementieren.
ModelWithUUID und ModelWithRequiredUUID#
ModelWithUUID definiert eine UUID als Primärschlüssel:
class ModelWithUUID(ModelWithID):
uuid = properties.property(
types.UUID(),
read_only=True,
id_property=True,
default=lambda: uuid.uuid4(),
)
Beispiel:
class Foo(models.ModelWithUUID):
value = properties.property(types.Integer(), required=True)
foo = Foo(value=10)
print(foo.uuid) # automatisch generierte UUID
print(foo.get_id()) # identisch zu foo.uuid
ModelWithRequiredUUID ist ähnlich, verlangt aber eine explizit gesetzte UUID ohne Default.
Operational Storage#
DmOperationalStorage#
Ein kleines Hilfsobjekt, das von MetaModel als __operational_storage__ verwendet wird:
store(name, data)— speichert beliebige Daten unter einem Namen.get(name)— liest Daten oder wirftNotFoundOperationalStorageError.
Beispiel:
from restalchemy.dm import models
class Foo(models.ModelWithUUID):
pass
Foo.__operational_storage__.store("table_name", "foos")
assert Foo.__operational_storage__.get("table_name") == "foos"
Häufige Mixins#
ModelWithTimestamp#
Fügt created_at und updated_at Felder mit UTC-Zeit hinzu:
- Beide Felder sind Pflichtfelder, read-only und verwenden
types.UTCDateTimeZ(). update()aktualisiertupdated_atautomatisch, wenn das Modell "dirty" ist (oderforce=True).
class TimestampedFoo(models.ModelWithUUID, models.ModelWithTimestamp):
value = properties.property(types.Integer(), required=True)
ModelWithProject#
Fügt ein Pflichtfeld project_id vom Typ types.UUID() hinzu:
class ProjectResource(models.ModelWithUUID, models.ModelWithProject):
name = properties.property(types.String(max_length=255), required=True)
ModelWithTags#
Fügt ein Feld tags hinzu — eine standardmäßig leere Liste von
Zeichenketten —, um Zeilen mit Werten zu versehen, nach denen eine Abfrage
suchen kann:
class TaggedResource(models.ModelWithUUID, models.ModelWithTags):
name = properties.property(types.String(max_length=255), required=True)
Die Spalte ist ein PostgreSQL-Array, und die Suche darin braucht einen GIN-Index; die Migration, die die Tabelle anlegt, deklariert beides:
Gesucht wird aus Python mit
ContainsAll / ContainsAny oder über HTTP
mit einem Filterausdruck (?q=tags:"env:prod") — siehe
Sammlungen über HTTP filtern.
ModelWithNameDesc und ModelWithRequiredNameDesc#
Gemeinsame Felder name und description:
ModelWithNameDesc:name: String bis 255 Zeichen, Default"".description: String bis 255 Zeichen, Default"".ModelWithRequiredNameDesc:nameist Pflichtfeld.
Custom Properties und Simple Views#
CustomPropertiesMixin#
Erlaubt zusätzliche "Custom Properties" mit eigenen Typen:
__custom_properties__: Mapping Name → Typ (types.BaseType).get_custom_properties()liefert(name, type)Paare.get_custom_property_type(name)gibt den Typ eines Custom-Feldes zurück._check_custom_property_value()validiert Werte und kann statische Werte erzwingen.
DumpToSimpleViewMixin#
dump_to_simple_view() konvertiert ein Modell in eine Struktur aus einfachen Typen (für JSON, OpenAPI, Storage):
result = model.dump_to_simple_view(
skip=["internal_field"],
save_uuid=True,
custom_properties=False,
)
- Iteriert über
self.propertiesund nutztto_simple_type()des jeweiligen Typs. - Bei
save_uuid=Truewerden UUID-Felder als Strings ausgegeben. - Optional werden Custom-Properties konvertiert.
RestoreFromSimpleViewMixin#
restore_from_simple_view() baut ein Modell aus einer "Simple View":
user = User.restore_from_simple_view(
skip_unknown_fields=True,
name="Alice",
created_at="2006-01-02T15:04:05.000576Z",
)
Verhalten:
- Normalisiert Feldnamen (
-→_). - Kann unbekannte Felder ignorieren.
- Nutzt
from_simple_type()/from_unicode()der Typen.
SimpleViewMixin#
Kombiniert beide Mixins:
class User(models.ModelWithUUID, models.SimpleViewMixin):
name = properties.property(types.String(max_length=255), required=True)
Round-trip Beispiel:
Zusammenfassung#
- Verwenden Sie
Modeloder seine Helferklassen als Basis für alle DM-Modelle. - Nutzen Sie Mixins wie
ModelWithUUID,ModelWithTimestamp,ModelWithProject,ModelWithNameDesc, um wiederkehrende Muster abzubilden. - Verwenden Sie Simple-View-Mixins für die Konvertierung von Modellen in einfache Strukturen und zurück.