Riferimento alle proprietà delle entità

Datastore supporta diversi tipi di dati per i valori di proprietà. Tra questi, tra gli altri:

  • Interi
  • Numeri con virgola mobile
  • Stringhe
  • Date
  • Dati binari

Per un elenco completo dei tipi, consulta le Proprietà e i tipi di valore.

Proprietà e tipi di valore

I valori dei dati associati a un'entità sono costituiti da una o più proprietà. Ogni proprietà ha un nome e uno o più valori. Una proprietà può avere valori di più tipi e due entità possono avere valori di tipi diversi per la stessa proprietà. Le proprietà possono essere indicizzate o non indicizzate (le query che ordinano o filtrano una proprietà P ignoreranno le entità in cui P è non indicizzato). Un'entità può avere al massimo 20.000 proprietà indicizzate.

Sono supportati i seguenti tipi di valori:

Quando una query prevede una proprietà con valori di tipi misti, Datastore utilizza un ordinamento deterministico basato sulle rappresentazioni interne:

  1. Valori null
  2. Numeri a punto fisso
    • Interi
    • Date e ore
  3. Valori booleani
  4. Sequenze di byte
    • Stringa Unicode
    • Chiavi dell'archivio BLOB
  5. Numeri con virgola mobile
  6. Chiavi Datastore

Poiché le stringhe di testo lunghe e le stringhe con byte lunghi non sono indicizzate, non hanno un ordine definito.

Tipi di proprietà

NDB supporta i seguenti tipi di proprietà:

Tipo di immobile Descrizione
IntegerProperty Numero intero firmato a 64 bit
FloatProperty Numero a virgola mobile doppia precisione
BooleanProperty Booleano
StringProperty Stringa Unicode; fino a 1500 byte, indicizzato
TextProperty Stringa Unicode; lunghezza illimitata, non indicizzata
BlobProperty Stringa di byte non interpretata:
se imposti indexed=True, fino a 1500 byte, indicizzata;
se indexed è False (impostazione predefinita), lunghezza illimitata, non indicizzata.
Argomento con parole chiave facoltativo: compressed.
DateTimeProperty Data e ora (vedi Proprietà data e ora)
DateProperty Data (vedi Proprietà data e ora)
TimeProperty Ora (vedi Proprietà data e ora)
GeoPtProperty Posizione geografica. Questo è un oggetto ndb.GeoPt. L'oggetto ha attributi lat e lon, entrambi in virgola mobile. Puoi crearne una con due float come ndb.GeoPt(52.37, 4.88) o con una stringa ndb.GeoPt("52.37, 4.88"). (si tratta in realtà della stessa classe di db.GeoPt)
KeyProperty Chiave Datastore
Argomento della parola chiave facoltativo: type=kind, per richiedere che le chiavi assegnate a questa proprietà abbiano sempre il tipo indicato. Può essere una stringa o una sottoclasse Modello.
BlobKeyProperty Chiave Blobstore
Corrisponde a BlobReferenceProperty nella precedente API db, ma il valore della proprietà è BlobKey invece di BlobInfo; puoi creare un BlobInfo da questo elemento utilizzando BlobInfo(blobkey)
UserProperty Oggetto utente.
StructuredProperty Include un tipo di modello all'interno di un altro, per valore (vedi Proprietà strutturate)
LocalStructuredProperty Come StructuredProperty, ma la rappresentazione sul disco è un blob opaco e non è indicizzata (vedi le Proprietà strutturate).
Argomento con parole chiave facoltativo: compressed.
JsonProperty Il valore è un oggetto Python (ad esempio un elenco o un dettato o una stringa) che può essere serializzato utilizzando il modulo json di Python; Datastore archivia la serializzazione JSON come blob. Non indicizzato per impostazione predefinita.
Argomento con parole chiave facoltativo: compressed.
PickleProperty Il valore è un oggetto Python (ad esempio un elenco o un dettato o una stringa) che può essere serializzato utilizzando il protocollo pickle di Python; Datastore archivia la serializzazione pickle come blob. Non indicizzato per impostazione predefinita.
Argomento con parole chiave facoltativo: compressed.
GenericProperty Valore generico
Utilizzato principalmente dalla classe Expando, ma utilizzabile anche in modo esplicito. Può essere di tipo int, long, float, bool, str, unicode, datetime, Key, BlobKey, GeoPt, User, None.
ComputedProperty Valore calcolato da altre proprietà da una funzione definita dall'utente. (vedi Proprietà calcolate).

Alcune di queste proprietà hanno un argomento della parola chiave facoltativo, compressed. Se la proprietà ha compressed=True, i dati vengono compressi con gzip su disco. Richiede meno spazio, ma è necessaria una CPU per codificare/decodificare le operazioni di scrittura e lettura.

Sia la compressione che la decompressione sono "lazy"; un valore di proprietà compressa verrà decompresso solo al primo accesso. Se leggi un'entità contenente un valore di proprietà compressa e la riscrivi senza accedere alla proprietà compressa, questa non verrà decompressa né compressa. Anche la cache contestuale partecipa a questo schema lento, ma memcache memorizza sempre il valore compresso per le proprietà compresse.

A causa del tempo aggiuntivo della CPU necessario per la compressione, è consigliabile utilizzare proprietà compresse solo se i dati sarebbero troppo grandi per non supportarla. Ricorda che la compressione basata su gzip in genere non è efficace per immagini e altri dati multimediali, poiché questi formati sono già compressi utilizzando un algoritmo di compressione specifico per i contenuti multimediali (ad esempio JPEG per le immagini).

Opzioni proprietà

La maggior parte dei tipi di proprietà supporta alcuni argomenti standard. Il primo è un argomento di posizione facoltativo che specifica il nome del datastore della proprietà. Puoi utilizzarlo per assegnare alla proprietà un nome diverso in Datastore rispetto al punto di vista dell'applicazione. Un uso comune è ridurre lo spazio in Datastore, consentendo a Datastore di utilizzare nomi di proprietà abbreviati, mentre il codice ne utilizza di più lunghi e significativi. Ad esempio,

class Employee(ndb.Model):
    full_name = ndb.StringProperty('n')
    retirement_age = ndb.IntegerProperty('r')

Ciò è particolarmente utile per le proprietà ripetute per cui si prevede che molti valori per entità.

Inoltre, la maggior parte dei tipi di proprietà supporta i seguenti argomenti delle parole chiave:

Argomento Tipo predefinita Descrizione
indexed bool Normalmente True Includi la proprietà negli indici di Datastore; se False, non è possibile eseguire query sui valori, ma le scritture sono più veloci. Non tutti i tipi di proprietà supportano l'indicizzazione; l'impostazione di indexed su True non funziona.
Le proprietà non indicizzate costano meno operazioni di scrittura rispetto alle proprietà indicizzate.
repeated bool False Il valore della proprietà è un elenco Python contenente i valori del tipo sottostante (consulta la sezione Proprietà ripetute).
Non può essere combinato con required=True o default=True.
required bool False È necessario specificare un valore per la proprietà.
default Tipo sottostante della proprietà Nessuno Valore predefinito della proprietà se non specificato esplicitamente.
choices Elenco di valori del tipo sottostante None Elenco facoltativo di valori consentiti.
validator Funzione None

Funzione facoltativa per convalidare ed eventualmente forzare il valore.

Verranno richiamati con argomenti (prop, value) e dovrebbero restituire il valore (possibilmente forzato) o generare un'eccezione. Richiamare la funzione su un valore forzato non dovrebbe modificare ulteriormente il valore. (Ad esempio, la restituzione di value.strip() o value.lower() va bene, ma non di value + '$'.) Può anche restituire None, che significa "nessun cambio". Consulta anche la Scrittura nelle sottoclassi di proprietà

verbose_name string None

Etichetta HTML facoltativa da utilizzare nei framework web come jinja2.

Proprietà ripetute

Qualsiasi proprietà con repeated=True diventa una proprietà ripetuta. La proprietà accetta un elenco di valori del tipo sottostante, anziché un singolo valore. Ad esempio, il valore di una proprietà definita con IntegerProperty(repeated=True) è un elenco di numeri interi.

Datastore può visualizzare più valori per tale proprietà. Per ogni valore viene creato un record di indice separato. Ciò influisce sulla semantica delle query; consulta Esecuzione di query per le proprietà ripetute per un esempio.

In questo esempio viene utilizzata una proprietà ripetuta:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)
...
article = Article(
    title='Python versus Ruby',
    stars=3,
    tags=['python', 'ruby'])
article.put()

L'operazione crea un'entità Datastore con i seguenti contenuti:

assert article.title == 'Python versus Ruby'
assert article.stars == 3
assert sorted(article.tags) == sorted(['python', 'ruby'])

Quando esegui una query per la proprietà tags, questa entità soddisferà una query per 'python' o 'ruby'.

Quando aggiorni una proprietà ripetuta, puoi assegnarla a un nuovo elenco o modificare l'elenco esistente. Quando assegni un nuovo elenco, i tipi di elementi vengono convalidati immediatamente. I tipi di elementi non validi (ad esempio, l'assegnazione di [1, 2] a art.tags sopra) generano un'eccezione. Quando modifichi l'elenco, la modifica non viene convalidata immediatamente. Il valore verrà convalidato quando scrivi l'entità su Datastore.

Datastore conserva l'ordine degli elementi dell'elenco in una proprietà ripetuta, in modo che tu possa assegnare un significato al loro ordine.

Proprietà Data e ora

Sono disponibili tre tipi di proprietà per archiviare valori relativi a data e ora:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Questi utilizzano i valori appartenenti alle classi corrispondenti (date, time, datetime) del modulo datetime standard di Python. Il più generale dei tre è DateTimeProperty, che indica sia una data di calendario sia un'ora del giorno; gli altri sono occasionalmente utili per scopi speciali che richiedono solo una data (ad esempio una data di nascita) o solo un orario (ad esempio un orario di riunione). Per motivi tecnici, DateProperty e TimeProperty sono sottoclassi di DateTimeProperty, ma non dipendono da questa relazione di ereditarietà e tieni presente che è diversa dalle relazioni di ereditarietà tra le classi sottostanti definite dal modulo datetime stesso.

Nota: le ore di clock di App Engine sono sempre espresse in tempo universale coordinato (UTC). Questo diventa pertinente se utilizzi la data o l'ora corrente (datetime.datetime.now()) come valore o converti tra oggetti data e ora e timestamp POSIX o tuple temporali. Tuttavia, in Datastore non vengono memorizzate informazioni esplicite sui fusi orari, pertanto se utilizzi con cautela l'utilizzo di questi dati puoi rappresentare gli orari locali in qualsiasi fuso orario, se usi l'ora corrente o le conversioni.

Ciascuna di queste proprietà ha due opzioni aggiuntive di parole chiave booleane:

Opzione Descrizione
auto_now_add Imposta la proprietà sulla data/ora corrente quando viene creata l'entità. Puoi eseguire manualmente l'override di questa proprietà. Quando l'entità viene aggiornata, la proprietà non subisce modifiche. Per questo comportamento, utilizza auto_now.
auto_now Imposta la proprietà sulla data/ora corrente quando viene creata l'entità e ogni volta che viene aggiornata.

Queste opzioni non possono essere combinate con repeated=True. Per impostazione predefinita, entrambi sono impostati su False; se entrambi sono impostati su True, auto_now ha la precedenza. È possibile eseguire l'override del valore per una proprietà con auto_now_add=True, ma non per un valore con auto_now=True. Il valore automatico non viene generato finché l'entità non viene scritta, cioè queste opzioni non forniscono valori predefiniti dinamici. Questi dettagli sono diversi rispetto all'API precedente di database.

Nota: quando una transazione che scrive una proprietà con auto_now_add=True non va a buon fine e viene tentata in un secondo momento, verrà riutilizzato lo stesso valore temporale del tentativo originale, invece di aggiornarlo al momento del nuovo tentativo. Se la transazione ha esito negativo definitivamente, il valore della proprietà verrà comunque impostato nella copia in memoria dell'entità.

Proprietà strutturate

Puoi strutturare le proprietà di un modello. Ad esempio, puoi definire un contatto per la classe del modello contenente un elenco di indirizzi, ciascuno con struttura interna. Puoi utilizzare le proprietà strutturate (tipo StructuredProperty) ad esempio:

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()
...
class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)
...
guido = Contact(
    name='Guido',
    addresses=[
        Address(
            type='home',
            city='Amsterdam'),
        Address(
            type='work',
            street='Spear St',
            city='SF')])

guido.put()

L'operazione crea una singola entità Datastore con le seguenti proprietà:

assert guido.name == 'Guido'
addresses = guido.addresses
assert addresses[0].type == 'home'
assert addresses[1].type == 'work'
assert addresses[0].street is None
assert addresses[1].street == 'Spear St'
assert addresses[0].city == 'Amsterdam'
assert addresses[1].city == 'SF'

La lettura di tale entità ricostruisce esattamente l'entità originale Contact. Anche se le istanze Address sono definite utilizzando la stessa sintassi delle classi di modelli, non sono entità complete. Non hanno le proprie chiavi in Datastore. Non possono essere recuperate indipendentemente dall'entità Contact a cui appartengono. Un'applicazione può, tuttavia, eseguire query sui valori dei singoli campi. Consulta la pagina Filtrare i valori delle proprietà strutturate. Tieni presente che address.type, address.street e address.city vengono visualizzati come array paralleli dal punto di vista di Datastore, ma la libreria NDB nasconde questo aspetto e crea l'elenco corrispondente di istanze Address.

Puoi specificare le consuete opzioni delle proprietà per le proprietà strutturate (tranne indexed). In questo caso, il nome del datastore è l'argomento posizionale del secondo (la prima è la classe del modello utilizzata per definire la sottostruttura).

Quando non hai bisogno di chiedere una proprietà interna di una sottostruttura, puoi utilizzare una proprietà strutturata locale (LocalStructuredProperty). Se sostituisci StructuredProperty con LocalStructuredProperty nell'esempio precedente, il comportamento del codice Python è lo stesso, ma Datastore vede solo un blob opaco per ogni indirizzo. L'entità guido creata nell'esempio sarebbe archiviata come segue: nome = 'Guido' address = <opaque blob for {'type': 'home' 'city

L'entità verrà letta correttamente. Poiché le proprietà di questo tipo sono sempre non indicizzate, non puoi eseguire query sui valori degli indirizzi.

Nota: un elemento StructuredProperty con una proprietà nidificata (che sia strutturata o meno) supporta un solo livello di proprietà ripetute. L'elemento StructuredProperty può essere ripetuto, oppure la proprietà nidificata può essere ripetuta, ma non entrambe. Una soluzione alternativa consiste nell'utilizzare LocalStructuredProperty, che non ha questo vincolo (ma non consente query sui valori delle proprietà).

Proprietà calcolate

Le proprietà calcolate (ComputedProperty) sono proprietà di sola lettura il cui valore viene calcolato da altri valori di proprietà tramite una funzione fornita dall'applicazione. Tieni presente che una proprietà calcolata supporta solo i tipi supportati da proprietà generiche. Il valore calcolato viene scritto in Datastore in modo che possa essere interrogato e visualizzato nel visualizzatore di Datastore, ma il valore archiviato viene ignorato quando l'entità viene letta da Datastore; viene invece ricalcolato chiamando la funzione ogni volta che viene richiesto il valore. Ad esempio:

class SomeEntity(ndb.Model):
    name = ndb.StringProperty()
    name_lower = ndb.ComputedProperty(lambda self: self.name.lower())
...
entity = SomeEntity(name='Nick')
entity.put()

Questo memorizza un'entità con i seguenti valori di proprietà:

assert entity.name == 'Nick'
assert entity.name_lower == 'nick'

Se modifichiamo il nome in 'Nickie' e chiediamo il valore di name_lower, restituisce 'nickie':

entity.name = 'Nick'
assert entity.name_lower == 'nick'
entity.name = 'Nickie'
assert entity.name_lower == 'nickie'

Nota: utilizza ComputedProperty se l'applicazione query per il valore calcolato. Se vuoi semplicemente utilizzare la versione derivata in codice Python, definisci un metodo regolare o utilizza il codice @property integrato da Python.

Nota: Se crei un modello senza specificare manualmente una chiave e ti affidi invece a Datastore per generare automaticamente l'ID dell'entità, sul primo put() ComputedProperty non sarà in grado di leggere il campo ID poiché il campo viene calcolato prima che venga generato l'ID. Se hai bisogno di un ComputedProperty che utilizza l'ID dell'entità, puoi utilizzare il metodo allocate_ids per generare un ID e una chiave con cui creare l'entità, in modo che ComputedProperty possa fare riferimento a tale ID nel primo elemento Put() dell'entità.

Proprietà dei messaggi RPC del protocollo Google

La libreria Google Protocol RPC utilizza gli oggetti Message per i dati strutturati e può rappresentare richieste, risposte o altri elementi RPC. NDB fornisce un'API per l'archiviazione degli oggetti Message del protocollo Google RPC come proprietà dell'entità. Supponi di definire una sottoclasse Message:

from protorpc import messages
...
class Note(messages.Message):
    text = messages.StringField(1, required=True)
    when = messages.IntegerField(2)

Puoi archiviare gli oggetti Note in Datastore come valori delle proprietà dell'entità utilizzando l'API msgprop di NDB.

from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
...
class NoteStore(ndb.Model):
    note = msgprop.MessageProperty(Note, indexed_fields=['when'])
    name = ndb.StringProperty()
...
my_note = Note(text='Excellent note', when=50)

ns = NoteStore(note=my_note, name='excellent')
key = ns.put()

new_notes = NoteStore.query(NoteStore.note.when >= 10).fetch()

Se vuoi eseguire query per i nomi dei campi, devi indicizzarli. Puoi specificare un elenco di nomi di campo che verranno indicizzati con il parametro indexed_fields in MessageProperty.

MessageProperty supporta molte opzioni di proprietà, ma non tutte. Supporta:

  • name
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

Le proprietà dei messaggi non supportano l'opzione della proprietà indexed; non puoi indicizzare i valori di Message. (Puoi indicizzare i campi di un messaggio come descritto sopra).

Sono validi anche i messaggi nidificati (utilizzando MessageField):

class Notebook(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)
...
class SignedStorableNotebook(ndb.Model):
    author = ndb.StringProperty()
    nb = msgprop.MessageProperty(
        Notebook, indexed_fields=['notes.text', 'notes.when'])

MessageProperty ha un'opzione speciale della proprietà, protocol, che specifica le modalità di serializzazione dell'oggetto del messaggio in Datastore. I valori sono i nomi di protocollo utilizzati dalla classe protorpc.remote.Protocols. I nomi di protocollo supportati sono protobuf e protojson; il valore predefinito è protobuf.

msgprop definisce anche EnumProperty, un tipo di proprietà che può essere utilizzato per archiviare un valore protorpc.messages.Enum in un'entità. Esempio:

class Color(messages.Enum):
    RED = 620
    GREEN = 495
    BLUE = 450
...
class Part(ndb.Model):
    name = ndb.StringProperty()
    color = msgprop.EnumProperty(Color, required=True)
...
p1 = Part(name='foo', color=Color.RED)
print p1.color  # prints "RED"

EnumProperty archivia il valore come numero intero; in effetti, EnumProperty è una sottoclasse di IntegerProperty. Ciò implica che puoi rinominare i valori enum senza dover modificare le entità già archiviate, ma non puoi rinumerarle.

La proprietà EnumProperty supporta le seguenti opzioni della proprietà:

  • name
  • indexed
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

Informazioni sui modelli di entità NDB

Un modello di entità NDB può definire proprietà. Le proprietà delle entità sono un po' come i membri di dati delle classi Python, un modo strutturato per contenere dati; sono anche un po' come i campi in uno schema di database.

Un'applicazione tipica definisce un modello di dati definendo una classe che eredita da Model con alcuni attributi della classe di proprietà. Ad esempio,


from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

In questo caso, username, userid e email sono proprietà di Account.

Esistono diversi altri tipi di proprietà. Alcuni sono utili per rappresentare date e orari e hanno comode funzionalità di aggiornamento automatico.

Un'applicazione può regolare il comportamento di una proprietà specificando opzioni per la proprietà; in questo modo può semplificare la convalida, impostare i valori predefiniti o modificare l'indicizzazione delle query.

Un modello può avere proprietà più complesse. Le proprietà ripetute sono simili a quelle di un elenco. Le proprietà strutturate sono simili a oggetti. Le proprietà calcolate in sola lettura vengono definite tramite funzioni. Ciò semplifica la definizione di una proprietà in termini di una o più altre proprietà. I modelli Expando possono definire le proprietà in modo dinamico.