App Engine(Python)Datastore Precall API挂钩 [英] App Engine (Python) Datastore Precall API Hooks

查看:201
本文介绍了App Engine(Python)Datastore Precall API挂钩的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



所以我们假设我正在为GAE开发应用程序,并且我想使用 API Hooks



BIG EDIT :在这个问题的原始版本中,我描述了我的用例,但是一些人正确地指出它并不真正适用于API Hooks。诚然!考虑我的帮助。但现在我的问题是学术问题:我仍然不知道如何在实践中使用钩子,我想。我已经重写了我的问题,以使其更具通用性。




代码



因此,我制作了一个这样的模型:

  class Model(db.Model ):
user = db.UserProperty(required = True)
def pre_put(self):
#设置一个值,引发异常,不管。使用你的想象力

然后我创建一个db_hooks.py:

  from google.appengine.api import apiproxy_stub_map 

def patch_appengine():
def hook(service,call,request,response ):
assert service =='datastore_v3'
如果调用=='Put':
用于request.entity_list()中的实体:
entity.pre_put()

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

由于TDD-addled,我正在使用 GAEUnit ,所以在gaeunit.py中,在主要方法的上方,我添加:

  import db_hooks 
db_hooks.patch_appengine()

然后我写出ea测试,实例化并放置模型。






问题


虽然 patch_appengine()肯定会被调用,但从不是。我错过了什么?如何让pre_put函数实际调用?

解决方案

这里的问题是在 hook()函数 entity 不是您所期望的db.Model的实例。

在这种情况下, entity 是被混淆地称为entity( entity_pb )。把它想象成你的真实实体的JSON表示,所有的数据都在那里,你可以从它建立一个新的实例,但是没有引用你正在等待的内存驻留实例它是回调函数。



所有各种 put / delete 方法的Monkey修补是设置模型级别的最佳方法回调,据我所知†



由于似乎没有那么多的资源如何安全地使用较新的异步调用来实现这一点,这里是一个BaseModel,它实现了before_put,after_put,before_delete& after_delete挂钩:

  class HookedModel(db.Model):

def before_put(self):
logging.error(放之前)

def after_put(self):
logging.error(放后)

def before_delete自我):
logging.error(删除前)

def after_delete(self):
logging.error(删除后)

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

def delete(self):
return self.delete_async()。get_result()
$ b $ def put_async(self):
return db.put_async(self)
$ b $ def delete_async(self):
return db.delete_async(self)

从HookedModel继承模型类并根据需要覆盖before_xxx,after_xxx方法。

将以下代码放置在应用程序中全局加载的位置(如 main.py ),如果您使用非常标准的外观布局)。这是调用我们的钩子的部分:

pre $ def normalize_entities(实体):
如果不是isinstance(entities,(列表,元组)):
实体=(实体)
返回[实体中的e,如果hasattr(e,'before_put')]

#monkeypatch put_async entity.before_put
db_put_async = db.put_async
def db_put_async_hooked(实体,** kwargs):
ents = normalize_entities(实体)
实体实体:
实体.before_put()
a = db_put_async(entities,** kwargs)
get_result = a.get_result
def get_result_with_callback():
用于实体的实体:
实体。 after_put()
return get_result()
a.get_result = get_result_with_callback
返回a
db.put_async = db_put_async_hooked


#monkeypatch delete_async调用entity.before_delete
db_delete_async = db.delete_async
def db_delete_async_hooked(entities,** kwa

ents = normalize_entities(实体)
实体的实体:
entity.before_delete()
a = db_delete_async(实体,** kwargs)
get_result = a.get_result
def get_result_with_callback():
用于实体:
entity.after_delete()
返回get_result()
a.get_result = get_result_with_callback
返回
db.delete_async = db_delete_async_hooked

您可以通过模型保存或销毁实例.put()或任何的db.put(),db.put_async()等方法,并获得所需的效果。



知道是否有更好的解决方案!?


Background

So let's say I'm making app for GAE, and I want to use API Hooks.

BIG EDIT: In the original version of this question, I described my use case, but some folks correctly pointed out that it was not really suited for API Hooks. Granted! Consider me helped. But now my issue is academic: I still don't know how to use hooks in practice, and I'd like to. I've rewritten my question to make it much more generic.


Code

So I make a model like this:

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

And then I create a 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')

Being TDD-addled, I'm making all this using GAEUnit, so in gaeunit.py, just above the main method, I add:

import db_hooks
db_hooks.patch_appengine()

And then I write a test that instantiates and puts a Model.


Question

While patch_appengine() is definitely being called, the hook never is. What am I missing? How do I make the pre_put function actually get called?

解决方案

The issue here is that within the context of the hook() function an entity is not an instance of db.Model as you are expecting.

In this context entity is the protocol buffer class confusingly referred to as entity (entity_pb). Think of it like a JSON representation of your real entity, all the data is there, and you could build a new instance from it, but there is no reference to your memory-resident instance that is waiting for it's callback.

Monkey patching all of the various put/delete methods is the best way to setup Model-level callbacks as far as I know†

Since there doesn't seem to be that many resources on how to do this safely with the newer async calls, here's a BaseModel that implements before_put, after_put, before_delete & after_delete hooks:

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)

Inherit your model-classes from HookedModel and override the before_xxx,after_xxx methods as required.

Place the following code somewhere that will get loaded globally in your applicaiton (like main.py if you use a pretty standard looking layout). This is the part that calls our hooks:

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

You can save or destroy your instances via model.put() or any of the db.put(), db.put_async() etc, methods and get the desired effect.

†would love to know if there is an even better solution!?

这篇关于App Engine(Python)Datastore Precall API挂钩的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆