App Engine(Python)Datastore Precall API挂钩 [英] App Engine (Python) Datastore Precall API Hooks
问题描述
所以我们假设我正在为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屋!