2012-08-23 12 views
13

Estoy en proceso de migración de db.Model a ndb.Model. El único problema que tengo que resolver antes de finalizar esta migración es que no existe el método Model.is_saved. He utilizado db.Model.is_saved en mi solicitud para determinar si los contadores fragmentados deben actualizarse en put/delete, para comprobar si hay llaves en conflicto en la creación de entidades etc.¿Es posible determinar con NDB si el modelo es persistente en el almacén de datos o no?

La documentación dice que ndb.Model no tiene equivalente para is_saved método. Puedo volver a implementar algunos casos de uso con get_or_insert en lugar de is_saved. Pero no todos ellos.

Como un hack sucio puedo establecer una bandera como _in_memory_instance para cada instancia que he creado llamando al constructor. Pero no resuelve mi problema. Todavía tengo que actualizar esta bandera al menos después de cada llamada a put().

La pregunta es: ¿hay una mejor manera de determinar si el modelo es persistente en el almacén de datos o no sin un hit extra del almacén de datos?

Edición 1: Se olvidó de mencionar: todas las entidades obtuvieron claves, así que verifique Model._has_complete_key() no funciona para mí.

Editar 2: después de esta discusión https://groups.google.com/d/topic/google-appengine/Tm8NDWIvc70/discussion parece ser la única manera de resolver mi problema es utilizar _post_get_hook/_post_put_hook. Me pregunto por qué una cosa tan trivial no se incluyó en la API oficial.

Editar 3: Terminé con la próxima clase base para todos mis modelos. Ahora puedo salir de mi base de código (casi) sin tocar:

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

¿Puedes verificar si el objeto tiene una clave/identificación? (posiblemente en una cláusula try/except). No estoy familiarizado con la clase de modelo NDB, pero recuerdo que db.Model genera un error (o devuelve None?) si intenta obtener la id/clave de una instancia de modelo antes de que se guarde (o posiblemente devuelva 'None'). No creas que eso también terminaría con un Datastore. Si eso funciona, podrías simplemente escribir una propiedad que sea algo como 'def is_saved (self): return self.key() es None' (o hacerlo con una cláusula try/except que devuelva True si se produce una excepción y False de lo contrario) –

+0

Verificar la clave no funcionará si está creando una clave. Una posible alternativa (pero puede ser insuficiente) es tener un indicador de fecha de creación (establecido en auto_now_add = True). From the docs "El valor automático no se genera hasta que se escribe la entidad, es decir, estas opciones no proporcionan valores predeterminados dinámicos". Sin embargo, si la escritura falla, se establecerá ese valor. Aunque deberías saber que la escritura falló ;-) –

+1

alternativamente usa un _post_put_hook para establecer tu bandera indicando que se guardó. –

Respuesta

11

Para obtener el mismo tipo de estado en NDB, necesitaría una combinación de post-get-hook y post-put-hook para establecer un indicador. Aquí hay un ejemplo de trabajo :

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 

No hay necesidad de comprobar el estado del futuro - ya sea cuando se llama al gancho , el futuro siempre tiene un resultado. Esto se debe a que el gancho es en realidad una devolución de llamada en el futuro. Sin embargo, es necesario verificar si el resultado es Ninguno.

PD: Dentro de una transacción, los ganchos se llaman tan pronto como la llamada put() retorna; El éxito o el fracaso de la transacción no entra en afectarlos. Consulte https://developers.google.com/appengine/docs/python/ndb/contextclass#Context_call_on_commit para ver una forma de ejecutar un enlace después de una confirmación correcta.

+5

Según lo analizado [aquí] (https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=211) esto no funcionará para entidades recuperadas a través de consultas/gql porque _post_get_hook no es los invocó y no hay devolución de llamada equivalente (todavía) para las consultas. –

2

Sobre la base de la idea @Tim Hoffmans puede que un puesto de gancho de este 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 

que no puede garantizar que se persistió en el Almacén de datos. No encontré nada al respecto en google :)

Por un lado, ver si una instancia de ndb.Model tiene una clave probablemente no funcione ya que una nueva instancia parece obtener una clave antes de enviarla al almacén de datos. Puede consultar el source code para ver qué sucede cuando crea una instancia de la clase ndb.Model.

+1

Está lo suficientemente cerca como para usarla como solución alternativa. Entonces yo voto +1. Aceptaré tu respuesta en un par de días si no aparece una mejor solución. – Sergey

+0

El único inconveniente que veo con este enfoque es que si una entidad ha sido recuperada en lugar de creada, 'is_saved' será' False'. Lo cual no se correlaciona con el comportamiento 'db.Model' de' is_saved() '. Para completarse, es probable que desee agregar un método '_post_get_hook' que también establece is_saved en True. –

Cuestiones relacionadas