检测应用引擎数据存储模型属性更改

时间:2014-08-15 01:18:22

标签: python google-app-engine google-cloud-datastore

每次数据存储区实体属性发生变化时如何触发方法调用?

我调查的一种方法是monkeypatching db.Model.put。这涉及到覆盖put方法。虽然这允许我对每个put()作出反应,但是我不清楚如何检测地址属性是否已经改变,因为self.address已经在.put()的开头设置了。

精化:

我有用户,每个用户都有一个物理地址。

class User(db.Model):
    ...
    address = db.StringProperty() # for example "2 Macquarie Street, Sydney"
    ...

我想验证输入的地址是否正确。为此,我有一个昂贵的地址检查功能(它与远程API联系)和一个布尔字段。

class User(db.Model):
    ...
    address = db.StringProperty()
    address_is_valid = db.BooleanProperty(default=False)

    def address_has_changed(self):
        self.address_is_valid = False
        Task(
            url = "/check_address", # this would later set .address_is_valid 
            params = {
                'user' : self.key()
            }
        ).add()
    ...

但是,如何在每次地址更改时触发address_has_changed方法,而不必在任何地方显式调用它?

# It should work when changing an address
some_user = User.all().get()
some_user.address = "Santa Claus Main Post Office, FI-96930 Arctic Circle"
some_user.put()

# It should also work when multiple models are changed
...
db.put([some_user, another_user, yet_another_user])

# It should even work when creating a user
sherlock = User(address='221 B Baker St, London, England')
sherlock.put() # this should trigger address_has_changed

4 个答案:

答案 0 :(得分:3)

Hook怎么样?

  

NDB提供轻量级挂钩机制。通过定义一个钩子,一个   应用程序可以在某种类型的操作之前或之后运行一些代码;   例如,Model可能会在每个get()之前运行一些函数。

from google.appengine.ext import ndb

class Friend(ndb.Model):
  name = ndb.StringProperty()

  def _pre_put_hook(self):
    # inform someone they have new friend

  @classmethod
  def _post_delete_hook(cls, key, future):
    # inform someone they have lost a friend

f = Friend()
f.name = 'Carole King'
f.put() # _pre_put_hook is called
fut = f.key.delete_async() # _post_delete_hook not yet called
fut.get_result() # _post_delete_hook is called

您可以构建一些其他逻辑,以便检查地址的原始版本和新版本,如果它们不同,则运行昂贵的操作,否则只需保存。

答案 1 :(得分:2)

好吧这可能是2年太晚了,但无论如何你总是可以在__init()__方法中创建类本地变量来存储旧值,而你在调用put方法时比较值从这些旧变量。

class User(db.Model):

    def __init__(self, *args, **kwargs):
        super(User, self).__init__(*args, **kwargs)
        self._old_address = self.address

    address = db.StringProperty() 

    def put(self, **kwargs):
        if self._old_address != self.address:
             # ...
             # do your thing
             # ...

        super(User, self).put(**kwargs)

答案 2 :(得分:1)

使用python属性。这样可以在实际更改时轻松调用address_has_changed。

答案 3 :(得分:1)

你所引用的Nick的文章或ndb钩子都没有解决跟踪实体中显式变化的问题,它们只是让它更容易解决。

当你调用put()时,你通常会在pre put hook中调用你的address_is_changed方法而不是整个代码库。

我有代码使用这些钩子策略来创建记录的每次更改的审计跟踪,

但是,您的代码实际上并未检测到对地址的更改。

您应该考虑更改为ndb,然后使用post_get hook(以远离您希望检查的原始属性值 - 例如在会话或请求对象中),然后使用pre_put_hook要检查当前属性与原始值,看看是否应该采取任何操作,然后调用ddress_has_changed方法。您可以使用db(通过遵循Nicks文章)来使用此策略,但是您必须自己做更多繁重的工作。