La classe Property
è progettata per essere creata con sottoclassi.
Tuttavia, in genere è più semplice creare una sottoclasse
Property
sottoclasse.
Tutti gli attributi Property
speciali,
anche quelli considerati "pubblici",
hanno nomi che iniziano con un trattino basso.
Questo perché StructuredProperty
utilizza lo spazio dei nomi dell'attributo senza trattino basso per fare riferimento
Property
nomi; Ciò è essenziale per specificare le query
proprietà secondarie.
La classe Property
e le sue sottoclassi predefinite consentono
creazione di sottoclassi mediante la convalida componibili (o impilabili)
API di conversione. Queste richiedono alcune definizioni terminologiche:
- Un user value è un valore che verrebbe impostato e a cui potrebbe accedere il del codice dell'applicazione utilizzando attributi standard sull'entità.
- Un valore di base è un valore come quello che verrebbe serializzato su e deserializzato da Datastore.
Una sottoclasse Property
che implementa una specifica
la trasformazione tra i valori utente e quelli serializzabili
a implementare due metodi, _to_base_type()
e
_from_base_type()
.
Questi non devono chiamare il proprio
super()
.
Questo è ciò che si intende per API componibili (o impilabili).
L'API supporta classi di stacking con regole sempre più sofisticate
conversioni base utenti: la conversione base utente
dal più sofisticato al meno sofisticato,
la conversione da base a utente passa da meno sofisticata a più
sofisticato. Ad esempio, controlla la relazione tra
BlobProperty
, TextProperty
,
e StringProperty
.
Ad esempio, TextProperty
eredita da
BlobProperty
; il suo codice è piuttosto semplice perché
eredita gran parte del comportamento di cui ha bisogno.
Oltre a _to_base_type()
e
_from_base_type()
,
_validate()
è anche un'API componibile.
L'API di convalida distingue tra lax e
utente strict
e i relativi valori. L'insieme di valori lax è un soprainsieme dell'insieme di valori
e i relativi valori. Il metodo _validate()
accetta un valore permissivo e se necessario
lo converte in un valore preciso. Ciò significa che quando imposti
della proprietà, sono accettati valori permissivi, mentre quando si ottiene il
, verranno restituiti solo valori rigidi. In caso contrario
la conversione è necessaria, _validate()
può restituire Nessuna. Se l'argomento
non rientra nell'insieme dei valori lax accettati,
_validate()
dovrebbe aumentare
un'eccezione, preferibilmente TypeError
o
datastore_errors.BadValueError
.
_validate()
, _to_base_type()
,
e _from_base_type()
non devono gestire:
None
: non verranno chiamati conNone
(e se restituisce None, significa che il valore non ha bisogno di conversione).- Valori ripetuti: l'infrastruttura si occupa di chiamare
_from_base_type()
o_to_base_type()
per ogni voce dell'elenco in un valore ripetuto. - Distinguere i valori utente dai valori di base: l'infrastruttura gestisce chiamando le API componibili.
- Confronti: le operazioni di confronto richiamano
_to_base_type()
sul loro operando. - La distinzione tra i valori utente e di base:
infrastruttura garantisce che
_from_base_type()
verrà chiamato con un valore di base (senza wrapper) e Verrà chiamato_to_base_type()
con un valore utente.
Ad esempio, supponiamo di dover archiviare numeri interi molto lunghi.
Lo standard IntegerProperty
supporta soltanto (firmato)
Numeri interi a 64 bit.
La tua proprietà potrebbe memorizzare un numero intero più lungo come stringa. sarebbe
è utile che la classe della proprietà
gestisca la conversione.
Un'applicazione che utilizza la classe della proprietà potrebbe essere simile a
...
...
...
...
Sembra semplice e immediato. Dimostra inoltre
utilizzo di alcune opzioni della proprietà standard (predefinita, ripetuta); come
l'autore di LongIntegerProperty
, ne sarà felice
senza dover scrivere "boilerplate" per ottenere
funziona. È più facile definire una sottoclasse di un'altra proprietà,
esempio:
Quando imposti il valore di una proprietà su un'entità, ad es.
ent.abc = 42
, il tuo _validate()
e (se non genera un'eccezione) il valore
sono archiviati nell'entità. Quando scrivi l'entità in Datastore,
viene chiamato il metodo _to_base_type()
, convertendo
alla stringa. Quindi quel valore viene serializzato dalla classe base,
StringProperty
.
La catena inversa degli eventi si verifica quando l'entità viene letta
per il datastore. StringProperty
e Property
si occupano degli altri dettagli, come la serializzazione
e deserializzare la stringa, impostare il valore predefinito e gestire
valori della proprietà ripetuti.
In questo esempio, il supporto delle disuguaglianze (ovvero le query che usano <, <=, >, >=) richiedono un maggior lavoro. L'implementazione dell'esempio seguente impone una dimensione massima per il numero intero e Archivia i valori come stringhe a lunghezza fissa:
È possibile usarla come LongIntegerProperty
devi passare il numero di bit al costruttore della proprietà,
ad es. BoundedLongIntegerProperty(1024)
.
Puoi creare sottoclassi di altri tipi di proprietà in modi simili.
Questo approccio funziona anche per l'archiviazione dei dati strutturati.
Supponi di avere una classe Python FuzzyDate
che rappresenta un
intervallo di date; utilizza i campi first
e last
per memorizzare l'inizio e la fine dell'intervallo di date:
...
Puoi creare un valore FuzzyDateProperty
che deriva
StructuredProperty
. Purtroppo quest'ultima
usare le vecchie classi Python, ha bisogno di una sottoclasse Model
.
Definisci quindi una sottoclasse del modello come rappresentazione intermedia;
Quindi, crea una sottoclasse di StructuredProperty
che imposta come hardcoded l'argomento modelclass in FuzzyDateModel
,
e definisce _to_base_type()
e
_from_base_type()
metodi per convertire tra FuzzyDate
e
FuzzyDateModel
:
Un'applicazione potrebbe utilizzare questa classe in questo modo:
...
Supponi di voler accettare oggetti date
normali in
oltre a FuzzyDate
oggetti come valori per
FuzzyDateProperty
. Per farlo, modifica il _validate()
come segue:
Potresti invece creare la sottoclasse FuzzyDateProperty
nel seguente modo:
(supponendo che FuzzyDateProperty._validate()
come mostrato sopra).
Quando assegni un valore a un
Campo MaybeFuzzyDateProperty
,
sia MaybeFuzzyDateProperty._validate()
che
FuzzyDateProperty._validate()
vengono richiamati, in questo ordine.
Lo stesso vale per _to_base_type()
e
_from_base_type()
: i metodi in
come superclasse e sottoclasse vengono combinate in modo implicito.
(Non utilizzare super
per controllare il comportamento ereditato per questa impostazione.
Per questi tre metodi,
l'interazione è impercettibile e super
non fa ciò che vuoi).