2015-04-07 104 views
0

我想创建一个合适的post_create(也是post_get和post_put)挂钩,类似于我在应用程序的数据库版本上的挂钩。Google App Engine NDB post_create挂钩

不幸的是我不能使用has_complete_key。

这个问题很有名:在模型中缺少is_saved。

现在我已经实现了它这样的:除了post_create钩

class NdbStuff(HooksInterface): 

    def __init__(self, *args, **kwds): 
     super(NdbStuff, self).__init__(*args, **kwds) 
     self._is_saved = False 

    def _put_async(self, post_hooks=True, **ctx_options): 
     """ Implementation of pre/post create hooks. """ 

     if not self._is_saved: 
      self._pre_create_hook() 

     fut = super(NdbStuff, self)._put_async(**ctx_options) 

     if not self._is_saved: 
      fut._immediate_callbacks.insert(
       0, 
       (
        self._post_create_hook, 
        [fut], 
        {}, 
       ) 
      ) 
      self._is_saved = True 

     if post_hooks is False: 
      fut._immediate_callbacks = [] 

     return fut 

    put_async = _put_async 

    @classmethod 
    def _post_get_hook(cls, key, future): 
     obj = future.get_result() 

     if obj is not None: 
      obj._is_saved = True 

     cls._post_get(key, future) 

    def _post_put_hook(self, future): 
     if future.state == future.FINISHING: 
      self._is_saved = True 
     else: 
      self._is_saved = False 
     self._post_put(future) 

一切似乎工作。

每次使用put_async时,都会触发post_create,而不先检索对象。

我真的很感谢关于如何在创建对象后触发post_create_hook一次的线索。

+0

为什么不使用工厂来创建实体,并且在创建对象时将'_is_saved'显式设置为false。你甚至开始放之前。那么你不必尝试和__init__方法一起玩。 –

+0

@TimHoffman你能更具体一点吗? 我已经_is_saved设置在init和右后类声明和非解决方案的工作。当put_async正在完成时,is_saved只是未更新为True – user2091046

+0

请参阅下面的答案。 –

回答

2

我不确定为什么要创建NDBStuff类。

任何方式,如果你创建一个类的实例,并且你想跟踪_is_saved或类似的东西,使用工厂来控制属性的创建和设置,在这种情况下,跟踪_is_new更有意义。

class MyModel(ndb.Model): 

    some_prop = ndb.StringProperty() 

    def _pre_put_hook(self): 

     if getattr(self,'_is_new',None): 
      self._pre_create_hook() 
     # do something 

    def _pre_create_hook(self): 
     # do something on first save 
     log.info("First put for this object") 

    def _post_create_hook(self, future): 
     # do something 

    def _post_put_hook(self, future); 
     if getattr(self,'_is_new', None): 
      self._post_create_hook(future) 
      # Get rid of the flag on successful put, 
      # in case you make some changes and save again. 
      delattr(self,'_is_new') 

    @classmethod 
    def factory(cls,*args,**kwargs): 
     new_obj = cls(*args,**kwargs) 
     settattr(new_obj,'_is_new',True) 
     return new_obj 

然后

myobj = MyModel.factory(someargs) 
    myobj.put() 
    myobj.some_prop = 'test' 
    myobj.put() 

会拜访先放_pre_create_hook,而不是第二。

总是通过工厂创建实体,然后你总是会调用_pre_create_hook执行。

这有道理吗?

+0

NDBStuff仅仅是一个例子,它应该是其他NDB类的基类。 这是建立,除其他事情,使post_create可能,同时做put_async。 我曾想过类似于您的解决方案,但它需要对流和对象创建进行大量更改。这与初始化对象时添加标志(即foo = MyModel(** args,is_new = True))基本相同。另外,我担心post_put可能无法清除标志,因为它运行无论失败。 总结一下:我只是想在我的所有ndb类中添加一个漂亮干净的post_create。 – user2091046

+0

重写初始化等可能充满危险,除非您仔细计划。我刚刚读完该线程,并且guido的建议是重写'_from_pb'并设置标志以指示它尚未创建。他还提到使用工厂。你也可以用_post_get_hook来做同样的事情,设置一个标志 - not_new,然后取反检查,以便只在检索到对象时才设置。 –

+0

覆盖像'_from_pb'这样的私有方法似乎有点冒险。我正在考虑用'Query'做类似的事情 - 你对此有何看法? 我想我可能会使用工厂,如果查询选项将无法正常工作。 关于'_post_get_hook' - 它看起来不会在查询后触发,所以我认为它不会解决问题。 – user2091046