2012-08-23 11 views
13

Sono in corso di migrazione da db.Model a ndb.Model. L'unico problema che devo risolvere prima di completare questa migrazione è che non esiste un metodo Model.is_saved. Ho usato db.Model.is_saved nella mia applicazione per determinare se i contatori sharded devono essere aggiornati su put/delete, per verificare la presenza di chiavi in ​​conflitto sulla creazione di entità eccÈ possibile determinare con NDB se il modello è persistente nel datastore o no?

La documentazione dice che ndb.Model non ha equivalenti per is_saved metodo. Posso reimplementare alcuni casi d'uso con get_or_insert anziché is_saved. Ma non tutti loro.

Come un trucco sporco posso impostare il flag come _in_memory_instance per ogni istanza che ho creato chiamando il costruttore. Ma non risolve il mio problema. Devo ancora aggiornare questo flag almeno dopo ogni chiamata put().

La domanda è: c'è un modo migliore per determinare se il modello è persistente nel datastore o no senza hit di datastore extra?

Modifica 1: dimenticato di menzionare: tutte le entità hanno le chiavi quindi controllare per Model._has_complete_key() non funziona per me.

Modifica 2: dopo questa discussione https://groups.google.com/d/topic/google-appengine/Tm8NDWIvc70/discussion sembra essere l'unico modo per risolvere il mio problema è utilizzare _post_get_hook/_post_put_hook. Mi chiedo perché una cosa così banale non sia stata inclusa nell'API ufficiale.

Modifica 3: ho finito con la prossima classe di base per tutti i miei modelli. Ora posso lasciare la mia base di codice (quasi) intatta:

class BaseModel(ndb.Model): 

    @classmethod 
    def _post_get_hook(cls, key, future): 
     self = future.get_result() 
     if self: 
      self._is_saved = bool(key) 

    def _post_put_hook(self, future): 
     self._is_saved = future.state == future.FINISHING 

    def is_saved(self): 
     if self._has_complete_key(): 
      return getattr(self, "_is_saved", False) 
     return False 
+0

puoi verificare se l'oggetto ha effettivamente una chiave/id? (eventualmente in una clausola try/except). Non ho familiarità con la classe del modello NDB, ma ricordo che db.Model genera un errore (o restituisce None?) se si tenta di ottenere l'id/chiave di un'istanza del modello prima che venga salvata (o possibilmente restituisca "None"). Non pensare che finirebbe con un successo di Datastore. Se funziona, puoi semplicemente scrivere una proprietà che è qualcosa come 'def is_saved (self): return self.key() è None' (o farlo con una clausola try/except che restituisce True se viene sollevata un'eccezione e False altrimenti). –

+0

Il controllo della chiave non funzionerà se si crea una chiave. Una possibile alternativa (ma potrebbe essere insufficiente) è un flag creation_date (impostato su auto_now_add = True). Dalla documentazione "Il valore automatico non viene generato finché l'entità non viene scritta, ovvero, queste opzioni non forniscono valori predefiniti dinamici." Tuttavia se la scrittura fallisce, quel valore verrà impostato. Anche se dovresti sapere che la scrittura è fallita ;-) –

+1

usa alternativamente un _post_put_hook per impostare il tuo flag che indica che è stato salvato. –

risposta

11

Per ottenere lo stesso tipo di stato in NDB è necessaria una combinazione di post-get-hook e post-put-hook per impostare un flag. Ecco un lavoro esempio:

class Employee(ndb.Model): 
    <properties here> 

    saved = False # class variable provides default value 

    @classmethod 
    def _post_get_hook(cls, key, future): 
    obj = future.get_result() 
    if obj is not None: 
     # test needed because post_get_hook is called even if get() fails! 
     obj.saved = True 

    def _post_put_hook(self, future): 
    self.saved = True 

Non c'è bisogno di verificare lo stato del futuro - quando uno aggancio viene chiamato, il futuro ha sempre un risultato. Questo perché il gancio è in realtà una richiamata sul futuro. Tuttavia è necessario verificare se il risultato è Nessuno!

PS: All'interno di una transazione, gli hook vengono richiamati non appena viene restituita la chiamata put(); il successo o il fallimento della transazione non entra in effetto su di loro. Vedere https://developers.google.com/appengine/docs/python/ndb/contextclass#Context_call_on_commit per un modo per eseguire un hook dopo un commit riuscito.

+5

Come discusso [qui] (https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=211) questo non funzionerà per le entità recuperate tramite query/gql perché _post_get_hook non è li ha richiamati e non esiste ancora un callback equivalente per le query. –

2

Sulla base @ Tim Hoffmans idea è possibile un gancio posto in questo modo:

class Article(ndb.Model): 
    title = ndb.StringProperty() 

    is_saved = False 

    def _post_put_hook(self, f): 
     if f.state == f.FINISHING: 
      self.is_saved = True 
     else: 
      self.is_saved = False 


article = Article() 
print article.is_saved ## False 
article.put() 
print article.is_saved ## True 

io non posso garantire che è persistito nel archivio dati. Non trovato nulla su google :)

Da un lato no, cercare di vedere se un'istanza di ndb.Model ha una chiave non funzionerà probabilmente dal momento che una nuova istanza sembra ricevere una chiave prima che sia mai stata inviata al datastore. Puoi guardare lo source code per vedere cosa succede quando crei un'istanza della classe ndb.Model.

+1

È abbastanza vicino da poter essere utilizzata come soluzione alternativa. Quindi voto +1. Accetterò la tua risposta tra un paio di giorni se non apparirà una soluzione migliore. – Sergey

+0

L'unico svantaggio che vedo con questo approccio come al momento è se un'entità è stata recuperata anziché creata allora "is_saved' sarà' False'. Quale non è associato al comportamento 'db.Model' di' is_saved() '. Per essere completo probabilmente vorrai aggiungere un metodo '_post_get_hook' che imposta anche is_saved su True. –

Problemi correlati