Riferimento alle proprietà delle entità

Firestore in modalità Datastore (Datastore) supporta diversi tipi di dati per i valori delle proprietà. tra cui:

  • Numeri interi
  • Numeri in virgola mobile
  • Stringhe
  • Date
  • Dati binari

Per un elenco completo dei tipi, consulta Proprietà e tipi di valori.

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ù di un tipo 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 in base a una proprietà P ignorano le entità in cui P non è indicizzata). Un'entità può avere al massimo 20.000 proprietà indicizzate.

Sono supportati i seguenti tipi di valori:

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

  1. Valori null
  2. Numeri a virgola fissa
    • Numeri interi
    • Date e ore
  3. Valori booleani
  4. Sequenze di byte
    • Stringa Unicode
    • Chiavi dell'archivio BLOB
  5. Numeri in virgola mobile
  6. Chiavi del datastore

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

Tipi di proprietà

NDB supporta i seguenti tipi di proprietà:

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

Alcune di queste proprietà hanno un argomento facoltativo della parola chiave, compressed. Se la proprietà ha compressed=True, i relativi dati vengono compressi con gzip su disco. Occupa meno spazio, ma richiede CPU per l'encoding/decodifica durante le operazioni di scrittura e lettura.

Sia la compressione sia la decompressione sono "lazy"; un valore della proprietà compressa verrà decompreso solo la prima volta che accedi. Se leggi un'entità contenente un valore di proprietà compresso e lo scrivi di nuovo senza accedere alla proprietà compressa, non verrà decompresso e compresso. Anche la cache in-context partecipa a questo schema lazy, ma la memcache memorizza sempre il valore compresso per le proprietà compresse.

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

Opzioni proprietà

La maggior parte dei tipi di proprietà supporta alcuni argomenti standard. Il primo è un argomento posizionale 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 utilizzo comune di questa funzionalità è ridurre lo spazio in Datastore, consentendo a Datastore di utilizzare nomi di proprietà abbreviati mentre il codice utilizza nomi più lunghi e significativi. Ad esempio,

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

Questo è particolarmente utile per le proprietà ripetute per le quali prevedi molti valori per entità.

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

Argomento Tipo Predefinito Descrizione
indexed bool Di solito 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 va a buon fine per questi tipi di proprietà.
Le proprietà non indicizzate richiedono meno operazioni di scrittura rispetto alle proprietà indicizzate.
repeated bool False Il valore della proprietà è un elenco Python contenente i valori del tipo di base (vedi Proprietà ripetute).
Non può essere combinato con required=True o default=True.
required bool False Per la proprietà deve essere specificato un valore.
default Tipo sottostante della proprietà Nessuno Valore predefinito della proprietà se non specificato esplicitamente.
choices Elenco di valori del tipo sottostante None Elenco facoltativo dei valori consentiti.
validator Funzione None

Funzione facoltativa per convalidare ed eventualmente forzare il valore.

Verrà chiamata con gli argomenti (prop, value) e deve restituire il valore (eventualmente forzato) o sollevare un'eccezione. La chiamata di nuovo della funzione su un valore coercito non deve modificarlo ulteriormente. Ad esempio, è consentito restituire value.strip() o value.lower(), ma non value + '$'. Può anche restituire None, che significa "nessuna modifica". Vedi anche Scrittura nelle sottoclassi di proprietà

verbose_name string None

Etichetta HTML facoltativa da utilizzare in framework di moduli 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 potrebbe vedere più valori per una proprietà di questo tipo. Per ogni valore viene creato un record dell'indice separato. Questo influisce sulla semantica delle query. Per un esempio, consulta Eseguire query per proprietà ripetute.

Questo esempio utilizza 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()

Viene creata 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 assegnarle un nuovo elenco o modificare l'elenco esistente in situ. Quando assegni un nuovo elenco, i tipi di elementi dell'elenco 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à in Datastore.

Datastore conserva l'ordine degli elementi dell'elenco in una proprietà ripetuta, in modo da poter assegnare un significato al loro ordinamento.

Proprietà di data e ora

Per memorizzare i valori relativi a data e ora sono disponibili tre tipi di proprietà:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Questi valori appartengono alle classi corrispondenti (date, time, datetime) del modulo datetime di Python standard. 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'ora (ad esempio l'ora di una riunione). Per motivi tecnici, DateProperty e TimeProperty sono sottoclassi di DateTimeProperty, ma non devi fare affidamento su questa relazione di ereditarietà (tieni presente che è diversa dalle relazioni di ereditarietà tra le classi sottostanti definite dal modulo datetime stesso).

Nota: le ore dell'orologio di App Engine sono sempre espresse nel fuso orario universale coordinato (UTC). Questo diventa rilevante se utilizzi la data o l'ora corrente (datetime.datetime.now()) come valore o esegui la conversione tra oggetti DateTime e timestamp o tuple di tempo POSIX. Tuttavia, in Datastore non vengono memorizzate informazioni esplicite sui fusi orari, quindi, se fai attenzione, puoi utilizzarle per rappresentare le ore locali in qualsiasi fuso orario, se utilizzi l'ora corrente o le conversioni.

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

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

Queste opzioni non possono essere combinate con repeated=True. Per entrambi l'impostazione predefinita è False. Se entrambi sono impostati su True, auto_now ha la precedenza. È possibile sostituire il valore di una proprietà con auto_now_add=True, ma non con auto_now=True. Il valore automatico non viene generato finché l'entità non viene scritta, ovvero queste opzioni non forniscono valori predefiniti dinamici. Questi dettagli sono diversi dalla vecchia API db.

Nota: quando una transazione che scrive una proprietà conauto_now_add=True non va a buon fine e viene riprovata in un secondo momento, verrà riutilizzato lo stesso valore di tempo del tentativo originale anziché aggiornarlo al momento del nuovo tentativo. Se la transazione non va a buon fine definitivamente, il valore della proprietà verrà comunque impostato nella copia in memoria dell'entità.

Strutture

Puoi strutturare le proprietà di un modello. Ad esempio, puoi definire una classe di modello Contact contenente un elenco di indirizzi, ciascuno con una struttura interna. Le proprietà strutturate (tipo StructuredProperty) ti consentono di farlo, 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()

Viene creata 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 un'entità di questo tipo consente di ricostruire esattamente l'entità Contact originale. Sebbene le istanze Address siano definite utilizzando la stessa sintassi delle classi di modelli, non sono entità complete. Non hanno chiavi proprie in Datastore. Non possono essere recuperati indipendentemente dalla persona giuridica Contact a cui appartengono. Tuttavia, un'applicazione può eseguire query sui valori dei singoli campi. Consulta Filtrare per 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 genera l'elenco corrispondente di istanze Address.

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

Se non devi eseguire query sulle proprietà interne 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 verrà memorizzata come segue: name = 'Guido' address = <opaque blob for {'type': 'home', 'city': 'Amsterdam'}> address = <opaque blob for {'type': 'work', 'city': 'SF', 'street': 'Spear St'}>

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

Nota: un StructuredProperty con una proprietà nidificata (indipendentemente dal fatto che sia strutturata o meno) supporta solo un singolo livello di proprietà ripetute. StructuredProperty può essere ripetuto o la proprietà nidificata può essere ripetuta, ma non entrambe. Una soluzione alternativa è utilizzare LocalStructuredProperty, che non ha questa limitazione (ma non consente le query sui valori delle proprietà).

Proprietà calcolate

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

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

Viene memorizzata un'entità con i seguenti valori delle proprietà:

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

Se cambiamo il nome in "Nickie" e chiediamo il valore di name_lower, viene restituito "nickie":

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

Nota: utilizza ComputedProperty se l'applicazione esegue query sul valore calcolato. Se vuoi solo utilizzare la versione derivata nel codice Python, definisci un metodo normale o utilizza la funzione @property integrata di Python.

Nota: se crei un modello senza una chiave specificata manualmente e ti affidi a Datastore per generare automaticamente l'ID dell'entità, nella prima put() un ComputedProperty non potrà leggere il campo ID poiché viene calcolato prima della generazione dell'ID. Se hai bisogno di un ComputedProperty che utilizzi 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 il tuo ComputedProperty possa fare riferimento a quell'ID al primo put() dell'entità.

Proprietà dei messaggi Google Protocol RPC

La libreria Google Protocol RPC utilizza oggetti Message per i dati strutturati; possono rappresentare richieste RPC, risposte o altro. NDB fornisce un'API per archiviare gli oggetti Message Google Protocol RPC come proprietà delle entità. Supponiamo 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 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 sui nomi dei campi, questi devono essere indicizzati. Puoi specificare un elenco di nomi di campi che verranno indicizzati con il parametro indexed_fields in MessageProperty.

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

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

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

Funzionano anche i messaggi nidificati (con 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 di proprietà speciale, protocol, che specifica in che modo l'oggetto messaggio viene serializzato in Datastore. I valori sono i nomi dei protocolli utilizzati dalla classe protorpc.remote.Protocols. I nomi dei protocolli 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 memorizza il valore come numero intero; infatti, EnumProperty è una sottoclasse di IntegerProperty. Ciò implica che puoi rinominare i valori dell'enum senza dover modificare le entità già memorizzate, ma non puoi numerarle di nuovo.

La proprietà EnumProperty supporta le seguenti opzioni di 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 memorizzare i dati. Sono anche un po' come i campi in uno schema di database.

Un'applicazione tipica definisce un modello dei 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()

Qui, username, userid e email sono proprietà di Account.

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

Un'applicazione può modificare il comportamento di una proprietà specificandovi opzioni che possono 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 un elenco. Le proprietà strutturate sono simili agli oggetti. Le proprietà calcolate di sola lettura vengono definite tramite funzioni. In questo modo è facile definire una proprietà in termini di una o più altre proprietà. I modelli Expando possono definire le proprietà in modo dinamico.