检测应用引擎数据存储模型属性变化
如何在每次数据存储实体属性变化时触发方法调用?
我查找过的一种方法是修改 db.Model.put 方法。这意味着我要重写 put 方法。虽然这样可以让我对每次 put() 操作做出反应,但我不太清楚如何检测地址属性是否发生了变化,因为在 .put() 的开始,self.address 已经被设置好了。
详细说明:
我有用户,每个用户都有一个实际的地址。
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 个回答
你提到的Nick的文章和ndb
钩子并不能解决跟踪实体明确变化的问题,它们只是让解决这个问题变得更简单。
通常情况下,你会在预存储钩子(pre put hook)里面调用你的address_is_changed
方法,而不是在代码的各个地方都调用put()。
我有一些代码,利用这些钩子策略来记录每次记录的变化。
不过,你的代码实际上并没有检测到地址的变化。
你可以考虑切换到ndb
,然后使用post_get hook
(把你想要检查的原始属性值存起来,比如在会话或请求对象中),接着用pre_put_hook
来检查当前属性和原始值,看看是否需要采取任何行动,然后再调用你的address_has_changed
方法。你也可以使用这个策略在db
上(按照Nick的文章),但那样你就得自己做更多的工作。
使用Python的属性功能。这样一来,每当地址真的发生变化时,就可以很方便地调用address_has_changed这个方法。
那什么是钩子呢?
NDB提供了一种轻量级的钩子机制。通过定义一个钩子,应用程序可以在某些操作之前或之后运行一些代码;比如,一个模型在每次执行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
你可以加入一些额外的逻辑,这样就可以检查地址的原始版本和新版本,如果它们不同,就执行比较复杂的操作,否则就直接保存。
好吧,这个回答可能来得有点晚,差不多两年了,但无论如何还是要说一下。你可以在 __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)