2010-02-28 15 views
5

AntecedentesApp Engine (Python) Precall almacén de datos API ganchos

Así que digamos que estoy haciendo aplicación para GAE, y quiero usar API Hooks.

EDITAR GRANDE: En la versión original de esta cuestión, que describe mi caso de uso, pero algunas personas correctamente señaló que no era realmente adecuado para los ganchos de la API. ¡Concedido! Considérame ayudado. Pero ahora mi problema es académico: todavía no sé cómo usar ganchos en la práctica, y me gustaría. He reescrito mi pregunta para que sea mucho más genérica.


Código

así que hago un modelo como este:

class Model(db.Model): 
    user = db.UserProperty(required=True) 
    def pre_put(self): 
     # Sets a value, raises an exception, whatever. Use your imagination 

Y luego creo un db_hooks.py:

from google.appengine.api import apiproxy_stub_map 

def patch_appengine(): 
    def hook(service, call, request, response): 
     assert service == 'datastore_v3' 
     if call == 'Put': 
      for entity in request.entity_list(): 
       entity.pre_put() 

    apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('preput', 
                 hook, 
                 'datastore_v3') 

Ser TDD de juergas, Estoy haciendo todo esto usando GAEUnit, entonces en gaeunit.py, justo arriba t El método principal, agrego:

import db_hooks 
db_hooks.patch_appengine() 

Y luego escribo una prueba que ejemplifica y pone un modelo.


pregunta es sin duda ser llamado

Mientras patch_appengine(), el gancho no es nunca. ¿Qué me estoy perdiendo? ¿Cómo realizo que se llame realmente a la función pre_put?

Respuesta

1

No creo que los enganches realmente vayan a resolver este problema. Los enganches solo se ejecutarán en el contexto de su aplicación de AppEngine, pero el usuario puede cambiar su alias fuera de su aplicación usando la configuración de la cuenta de Google. Si lo hacen, no activará ningún implemento lógico en sus ganchos.

Creo que la verdadera solución a su problema es que su aplicación administre su propio alias que es independiente del expuesto por la entidad de los Usuarios.

+0

Ah, maldición, tienes toda la razón. Entonces, ¿qué hay de hacer cumplir la singularidad por cuenta? ¿No sería mejor hacerlo al verificar antes de un put? –

+0

Si desea imponer la exclusividad, debe verificar antes de la primera puesta que crea la entidad en el almacén de datos. Si prohíbe al usuario cambiar ese valor, lo cual es una buena idea, solo necesita verificar la creación. –

+0

Cierto, y en ambos casos, solo voy a escribir un método que haga las comprobaciones y actualizaciones que quiero antes de poner, luego lo uso en lugar de put(). Gracias. –

2

Los ganchos son un poco bajo nivel para la tarea en cuestión. Lo que probablemente quiera es una clase de propiedad personalizada. DerivedProperty, de aetycoon, es solo el boleto.

Tenga en cuenta, sin embargo, que el campo "sobrenombre" del objeto de usuario probablemente no sea el que desea: por cada the docs, simplemente es la parte del usuario del campo de correo electrónico si usa una cuenta de Gmail; de lo contrario, es su dirección de correo electrónico completa. Es probable que desee permitir a los usuarios establecer sus propios apodos, en su lugar.

2

El problema aquí es que dentro del contexto de la función hook(), entity no es una instancia de db.Model como está esperando.

En este contexto entity es la clase de búfer de protocolo llamada confusamente entidad (entity_pb).Piense en ello como una representación JSON de su entidad real, todos los datos están allí, y podría crear una instancia nueva, pero no hay ninguna referencia a su instancia residente en memoria que está esperando su devolución de llamada.

mono parchear todos los diversos métodos put/delete es la mejor manera de configurar las devoluciones de llamada a nivel de modelo por lo que yo sé †

Dado que no parece ser que muchos recursos sobre cómo hacer esto de manera segura con las llamadas asíncronas más nuevos, aquí hay un BaseModel que implementa before_put, after_put, before_delete & ganchos after_delete:

class HookedModel(db.Model): 

    def before_put(self): 
     logging.error("before put") 

    def after_put(self): 
     logging.error("after put") 

    def before_delete(self): 
     logging.error("before delete") 

    def after_delete(self): 
     logging.error("after delete") 

    def put(self): 
     return self.put_async().get_result() 

    def delete(self): 
     return self.delete_async().get_result() 

    def put_async(self): 
     return db.put_async(self) 

    def delete_async(self): 
     return db.delete_async(self) 

herede modelo clases de HookedModel y redefinir la before_xxx, after_xxx métodos según sea necesario.

Coloque el siguiente código en algún lugar que se cargue globalmente en su aplicación (como main.py si utiliza un diseño de aspecto bastante estándar). Esta es la parte que llama a nuestros ganchos:

def normalize_entities(entities): 
    if not isinstance(entities, (list, tuple)): 
     entities = (entities,) 
    return [e for e in entities if hasattr(e, 'before_put')] 

# monkeypatch put_async to call entity.before_put 
db_put_async = db.put_async 
def db_put_async_hooked(entities, **kwargs): 
    ents = normalize_entities(entities) 
    for entity in ents: 
     entity.before_put() 
    a = db_put_async(entities, **kwargs) 
    get_result = a.get_result 
    def get_result_with_callback(): 
     for entity in ents: 
      entity.after_put() 
     return get_result() 
    a.get_result = get_result_with_callback 
    return a 
db.put_async = db_put_async_hooked 


# monkeypatch delete_async to call entity.before_delete 
db_delete_async = db.delete_async 
def db_delete_async_hooked(entities, **kwargs): 
    ents = normalize_entities(entities) 
    for entity in ents: 
     entity.before_delete() 
    a = db_delete_async(entities, **kwargs) 
    get_result = a.get_result 
    def get_result_with_callback(): 
     for entity in ents: 
      entity.after_delete() 
     return get_result() 
    a.get_result = get_result_with_callback 
    return a 
db.delete_async = db_delete_async_hooked 

Puede guardar o destruir las instancias a través de model.put() o cualquiera de los db.put(), db.put_async(), etc, métodos y obtener el efecto deseado.

† me encantaría saber si hay una solución aún mejor?

Cuestiones relacionadas