Creazione dei modelli di entità

Devi creare una classe di modello per la tua entità. Puoi eseguire questa operazione in due modi:

  • Crea una classe di modello che definisce le proprietà delle entità.
  • Crea una classe di modello expando che non definisce le entità in anticipo.

Questo documento descrive come creare ciascuno di questi tipi di classi di modelli. Descrive inoltre come creare un hook del modello, in modo che l'applicazione possa eseguire del codice prima o dopo un determinato tipo di operazioni, ad esempio prima di ogni get().

Creazione di una classe di modelli con proprietà

Prima di creare un'entità, devi creare una classe di modello che definisce una o più proprietà dell'entità. Ad esempio:

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

dove vuoi che ogni entità Account abbia proprietà per nome utente, ID utente e email.

Per un elenco completo dei tipi di proprietà, consulta la sezione Riferimento alle proprietà delle entità.

Creazione di una classe di modello Expando

Non è necessario utilizzare una classe modello che definisce le proprietà in anticipo. Una sottoclasse di modello speciale chiamata Expando modifica il comportamento delle sue entità in modo che qualsiasi attributo assegnato venga salvato nello datastore. Tieni presente che questi attributi non possono iniziare con un trattino basso.

Ecco come creare un modello Expando:

class Mine(ndb.Expando):
    pass
...
e = Mine()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
e.put()

Viene scritta un'entità nel datastore con una proprietà foo con valore intero 1, una proprietà bar con valore di stringa 'blah' e una proprietà tag ripetuta con valori di stringa 'exp', 'and' e 'oh'. Le proprietà sono indicizzate e puoi esaminarle utilizzando l'attributo _properties dell'entità:

return e._properties
# {
#     'foo': GenericProperty('foo'),
#     'bar': GenericProperty('bar'),
#     'tags': GenericProperty('tags', repeated=True)
# }

Un Expando creato recuperando un valore dal datastore ha proprietà per tutti i valori delle proprietà salvati nel datastore.

Un'applicazione può aggiungere proprietà predefinite a una sottoclasse Expando:

class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
...
employee = FlexEmployee(name='Sandy', location='SF')

In questo modo, a employee viene assegnato un attributo name con valore 'Sandy', un attributo age con valore None e un attributo dinamico location con valore 'SF'.

Per creare una sottoclasse Expando le cui proprietà non sono indicizzate, imposta _default_indexed = False nella definizione della sottoclasse:

class Specialized(ndb.Expando):
    _default_indexed = False
...
e = Specialized(foo='a', bar=['b'])
return e._properties
# {
#     'foo': GenericProperty('foo', indexed=False),
#     'bar': GenericProperty('bar', indexed=False, repeated=True)
# }

Puoi anche impostare _default_indexed su un'entità Expando. In questo caso, verrà applicata a tutte le proprietà assegnate dopo la sua impostazione.

Un'altra tecnica utile è eseguire query su un tipo Expando per una proprietà dinamica. Una query come la seguente

FlexEmployee.query(FlexEmployee.location == 'SF')

non funzionerà, perché la classe non ha un oggetto proprietà per la proprietà posizione. Utilizza invece GenericProperty, la classe utilizzata da Expando per le proprietà dinamiche:

FlexEmployee.query(ndb.GenericProperty('location') == 'SF')

Utilizzo degli hook del modello

NDB offre un meccanismo di hooking leggero. Con la definizione di un hook, un'applicazione può eseguire del codice prima o dopo un determinato tipo di operazioni. Ad esempio, un Model potrebbe eseguire una funzione prima di ogni get().

Una funzione di hook viene eseguita quando si utilizzano le versioni sincrone, asincrone e multiple del metodo appropriato. Ad esempio, un hook "pre-get" si applica a tutti get(), get_async() e get_multi(). Esistono versioni pre-RPC e post-RPC di ogni hook.

Gli hook possono essere utili per:

  • memorizzazione nella cache delle query
  • Controllo dell'attività di Cloud Datastore per utente
  • simulando gli attivatori del database

L'esempio seguente mostra come definire le funzioni di hook:

from google.appengine.ext import ndb
...
class Friend(ndb.Model):
    name = ndb.StringProperty()

    def _pre_put_hook(self):
        _notify('Gee wiz I have a new friend!')

    @classmethod
    def _post_delete_hook(cls, key, future):
        _notify('I have found occasion to rethink our friendship.')
...
f = Friend()
f.name = 'Carole King'
f.put()  # _pre_put_hook is called
fut = f.key.delete_async()  # _post_delete_hook not yet called
fut.get_result()  # _post_delete_hook is called

Se utilizzi i post-hook con API asincrone, gli hook vengono attivati chiamando check_result(), get_result() o restituendo (all'interno di un tasklet) il futuro di un metodo asincrono. I hook post non controllano se l'RPC è andata a buon fine; l'hook viene eseguito indipendentemente dall'errore.

Tutti i post-hook hanno un argomento Future alla fine della firma della chiamata. Questo oggetto Future contiene il risultato dell'azione. Puoi chiamare get_result() su questo Future per recuperare il risultato. Puoi essere certo che get_result() non si bloccherà, poiché Future è completato al momento della chiamata dell'hook.

L'attivazione di un'eccezione durante un pre-hook impedisce l'esecuzione della richiesta. Sebbene gli hook vengano attivati all'interno dei metodi <var>*</var>_async, non puoi eseguire la preemption di un'RPC sollevando tasklets.Return in un hook pre-RPC.

Passaggi successivi