Die Property
-Klasse ist als abgeleitete Klasse konzipiert.
In der Regel ist es jedoch einfacher, eine vorhandene abgeleitete Property
-Klasse als abgeleitete Klasse festzulegen.
Die Namen von speziellen Property
-Attributen beginnen mit einem Unterstrich. Dies trifft sogar auf Attribute zu, die als "öffentlich" gelten.
Dies liegt daran, dass StructuredProperty
den Attribut-Namespace ohne Unterstrich verwendet, um auf verschachtelte Property
-Namen zu verweisen. Dies ist für die Angabe von Abfragen für untergeordnete Properties unverzichtbar.
Die Property
-Klasse und ihre vordefinierten abgeleiteten Klassen ermöglichen ein einfaches Erstellen von abgeleiteten Klassen mithilfe von zusammensetzbaren (oder stapelbaren) Validierungs- und Konvertierungs-APIs. Einige Begriffe müssen in diesem Zusammenhang erläutert werden:
- Ein Nutzerwert ist ein Wert, der durch den Anwendungscode unter Verwendung von Standardattributen für die Entität festgelegt und abgerufen wird.
- Ein Basiswert ist ein Wert, der im Datenspeicher serialisiert und deserialisiert wird.
Eine abgeleitete Property
-Klasse zur Implementierung einer bestimmten Transformation zwischen Nutzerwerten und serialisierbaren Werten sollte zwei Methoden implementieren: _to_base_type()
und _from_base_type()
.
Diese sollten nicht die eigene Methode super()
aufrufen.
Hierbei handelt es sich um das Konzept, das mit zusammensetzbaren (oder stapelbaren) APIs gemeint ist.
Die API unterstützt stacking-Klassen mit immer komplexeren Konvertierungen zwischen Nutzer- und Basiswerten: Die Konvertierung von Nutzer- zu Basiswert geht von sehr komplex zu weniger komplex, die umgekehrte Konvertierung hingegen von weniger komplex zu sehr komplex. Siehe zum Beispiel die Beziehung zwischen BlobProperty
, TextProperty
und StringProperty
.
TextProperty
ist beispielsweise BlobProperty
untergeordnet. Dessen Code ist ziemlich einfach, da das erforderliche Verhalten größtenteils übernommen wird.
Neben _to_base_type()
und _from_base_type()
ist die Methode _validate()
auch eine zusammensetzbare API.
Die Validierungs-API unterscheidet zwischen laxen und strikten Nutzerwerten. Die Menge der laxen Werte ist eine Obermenge der Menge der strikten Werte. Die Methode _validate()
nimmt einen laxen Wert an und wandelt ihn bei Bedarf in einen strikten Wert um. Dies bedeutet, dass beim Festlegen des Property-Werts laxe Werte angenommen werden, während beim Abrufen des Property-Werts nur strikte Werte zurückgegeben werden. Wenn keine Konvertierung erforderlich ist, kann _validate()
den Wert "None" zurückgeben. Liegt das Argument außerhalb der Menge von akzeptierten laxen Werten, sollte _validate()
eine Ausnahme auslösen, vorzugsweise TypeError
oder datastore_errors.BadValueError
.
Die Parameter _validate()
, _to_base_type()
und _from_base_type()
müssen nicht verarbeitet werden:
None
: Sie werden nicht mitNone
aufgerufen. Wenn sie "None" zurückgeben, bedeutet dies, dass der Wert nicht konvertiert werden muss.- Wiederholte Werte: Die Infrastruktur übernimmt den Aufruf von
_from_base_type()
oder_to_base_type()
für jedes Listenelement in einem wiederholten Wert. - Unterscheidung zwischen Nutzerwerten und Basiswerten: Die Infrastruktur übernimmt dies durch Aufrufen der zusammensetzbaren APIs.
- Vergleiche: Die Vergleichsvorgänge rufen
_to_base_type()
auf ihrem Operanden auf. - Unterscheidung zwischen Nutzer- und Basiswerten: Die Infrastruktur gewährleistet, dass
_from_base_type()
mit einem (nicht eingebetteten) Basiswert und_to_base_type()
mit einem Nutzerwert aufgerufen wird.
Angenommen, Sie müssen sehr lange Ganzzahlen speichern.
Die Standard-IntegerProperty
unterstützt lediglich (signierte) 64-Bit-Ganzzahlen.
Ihre Eigenschaft speichert möglicherweise eine längere Ganzzahl als ein String. Es empfiehlt sich also die Übernahme der Konvertierung durch die Property-Klasse.
Eine Anwendung, die Ihre Property-Klasse verwendet, sieht in etwa folgendermaßen aus:
...
...
...
...
Das sieht einfach und unkompliziert aus. Es zeigt auch die Verwendung einiger standardmäßiger Attributoptionen (Standard, wiederholt). Als Autor von LongIntegerProperty
profitieren Sie davon, dass Sie keinen "Standardcode" schreiben müssen, um damit arbeiten zu können. Es ist einfacher, eine abgeleitete Klasse einer anderen Property zu definieren, zum Beispiel:
Wenn Sie einen Attributwert für eine Entität festlegen, z. B. ent.abc = 42
, wird Ihre Methode _validate()
aufgerufen und, wenn keine Ausnahme ausgelöst wird, in dem Wert gespeichert. Entität. Wenn Sie die Entität in den Datenspeicher schreiben, wird Ihre _to_base_type()
-Methode aufgerufen, wobei der Wert in den String konvertiert wird. Dann wird dieser Wert von der Basisklasse StringProperty
StringProperty serialisiert.
Die inverse Ereigniskette tritt auf, wenn die Entität aus dem Datenspeicher zurückgelesen wird. Die Klassen StringProperty
und Property
übernehmen die anderen Details, z. B. die Serialisierung und Deserialisierung des Strings, die Festlegung des Standardwerts und die Verarbeitung wiederholter Property-Werte.
In diesem Beispiel erfordert die Unterstützung von Ungleichungen (d. h. Abfragen mit <, <=, >, >=) mehr Arbeit. Die folgende Beispielimplementierung erzwingt eine maximale Größe von Ganzzahlen und speichert Werte als Strings fester Länge:
Dies kann auf die gleiche Weise wie LongIntegerProperty
verwendet werden, mit der Ausnahme, dass die Anzahl der Bit an den Attribut-Konstruktor übergeben werden müssen, z. B. BoundedLongIntegerProperty(1024)
.
Sie können andere Property-Typen auf ähnliche Weise ableiten.
Dieser Ansatz funktioniert auch beim Speichern strukturierter Daten.
Angenommen, Ihre Python-Klasse FuzzyDate
stellt einen Datumsbereich dar. Sie verwendet die Felder first
und last
, um den Anfang und das Ende des Datumsbereichs zu speichern:
...
Sie können ein FuzzyDateProperty
erstellen, das von StructuredProperty
abgeleitet wird. Leider kann das zuletzt genannte nicht mit einfachen alten Python-Klassen verwendet werden, sondern benötigt eine abgeleitete Model
-Klasse.
Definieren Sie eine abgeleitete Modellklasse als eine Zwischendarstellung.
Erstellen Sie als Nächstes eine abgeleitete Klasse von StructuredProperty
, die das modelclass-Argument als FuzzyDateModel
hartcodiert und die Methoden _to_base_type()
und _from_base_type()
für die Konvertierung zwischen FuzzyDate
und FuzzyDateModel
definiert:
Eine Anwendung könnte diese Klasse so verwenden:
...
Angenommen, Sie möchten einfache date
-Objekte zusätzlich zu FuzzyDate
-Objekten als Werte für FuzzyDateProperty
akzeptieren. Ändern Sie dazu die _validate()
-Methode so:
Sie können stattdessen FuzzyDateProperty
unter der Annahme, dass FuzzyDateProperty._validate()
der Darstellung oben entspricht, so ableiten:
Wenn Sie einem MaybeFuzzyDateProperty
-Feld einen Wert zuweisen, werden sowohl MaybeFuzzyDateProperty._validate()
als auch FuzzyDateProperty._validate()
in dieser Reihenfolge aufgerufen.
Das gleiche gilt für _to_base_type()
und _from_base_type()
: Die Methoden in der Basisklasse und abgeleiteten Klasse werden implizit kombiniert.
(Verwenden Sie hierfür nicht super
, um übernommenes Verhalten zu steuern.
Für diese drei Methoden ist die Interaktion subtil und super
verhält sich nicht wie gewünscht.)