如何读取_pre_put_hook中的旧属性值 [英] How to read old property values in a _pre_put_hook
问题描述
我正在尝试实施ndb模型审计,以便所有属性更改都存储在每个模型实例中。这是我选择实现的_pre_put_hook的代码。
def _pre_put_hook(self):
#save a更新的历史记录
如果不是(self.key是None或self.key.id()是None):
old_object = self.key.get(use_cache = True)
for attr在dir(self)中:
(如果不可调用)getattr(self,attr) ):
logging.debug('UPDATE:{0}'.format(attr))
logging.debug('OLD:{0} NEW:{1}'.format(getattr(old_object, attr),getattr(self,attr)))
问题是old_object始终填充相同自我(对象)的值正在更新。如何在put()被实际创建(_pre_put)之前访问旧对象的属性值?
编辑:
我意识到随着时间的推移,我正在做一堆不需要完成的工作(大量的CPU /内存用于复制整个实体并在不需要时传递它们)。这是更新后的版本,它存储了对原始protobuf的引用,并且只在需要时反序列化它。
__original = None#a shadow - 这个对象的拷贝,所以我们可以看到什么改变...懒惰膨胀
_original_pb = None#这个实体的原始编码Protobuf表示
@property
def _original(self ):
单例将protobuf反序列化为一个新的实体,它看起来像数据库中的原始数据
如果不是self .__ original和self._original_pb:
self .__ original = self .__ class __._ from_pb(self._original_pb)
return self .__ original
@classmethod
def _from_pb(cls,pb,set_key = True ,ent = None,key = None):
保存原始pb的副本,以便我们可以跟踪是否有任何东西在
entity = super ChangesetMixin,cls)._ from_pb(pb,set_key = set_key,ent = ent,key = key)
如果entity._origina l_pb是None而不是entity._projection:
#_from_pb会在我们取消一个新对象的时候被调用(就像通过延期库时一样)
#所以如果我们正在从pb物化并且我们不有一个关键,那么我们没有_original
实体.__ original = None
entity._original_pb = pb
返回实体
当您第一次阅读它时,对实体进行克隆:
将实体复制到在'编译'时间不知道属性名称的Python App Engine数据存储区
并将其放置在实体本身上,以便稍后在需要时引用它。这样,您不必为了进行比较而进行第二次数据存储阅读。
我们重写两个不同的Model方法来实现这一点:
@classmethod
def _post_get_hook(cls,key,future):
克隆这个实体,所以我们可以跟踪放置
之间是否有任何变化注意:只有在ndb.Key.get()之后才会调用... NOT从Query
加载时请参阅下面的_from_pb覆盖以了解完整图片
也注意到:在每个key.get()之后都会调用它,不管NDB是否已经缓存了
,这就是为什么我们只做clone()if _original没有设置...
entity = future.get_result()
如果实体不是None并且entity._original是None:
entity._original = clone (实体)
@classmethod
def _from_pb(cls,pb,set_key = True,ent = None,key = None):
clone this实体,所以我们可以跟踪一个在$ puts
之间有所变化这是一种知道何时从数据存储区加载对象的方法QUERY
_post_get_hook仅在直接Key.get()
上调用时没有文档化的钩子在查询结果后被调用
SEE:https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=211
entity = super(BaseModel,cls)._ from_pb(pb,set_key = set_key,ent = ent,key = key)
如果entity.key和entity._original是None:
#_from_pb将被调用如果我们取消一个新对象(就像通过延期库时一样)
#所以如果我们正在从pb物化并且没有键,那么我们没有_biginal
entity._original =克隆(实体)
返回实体
I am trying to implement an ndb model audit so that all changes to properties are stored within each model instance. Here is the code of the _pre_put_hook I chose to implement that.
def _pre_put_hook(self):
# save a history record for updates
if not (self.key is None or self.key.id() is None):
old_object = self.key.get(use_cache=True)
for attr in dir(self):
if not callable(getattr(self, attr)) and not attr.startswith("_"):
if getattr(self, attr) != getattr(old_object, attr):
logging.debug('UPDATE: {0}'.format(attr))
logging.debug('OLD: {0} NEW: {1}'.format(getattr(old_object, attr), getattr(self, attr)))
The problem is old_object is always populated with the same values of the self (object) being updated. How can I access the property values of the old object BEFORE the put() being actually made (_pre_put)?
EDIT:
I realized over time I was doing a bunch of work that didn't need to be done (alot of CPU/memory used copying entire entities and passing them around when may not be needed). Here's the updated version which stores a reference to the original protobuf and only deserializes it if you need it
__original = None # a shadow-copy of this object so we can see what changed... lazily inflated
_original_pb = None # the original encoded Protobuf representation of this entity
@property
def _original(self):
"""
Singleton to deserialize the protobuf into a new entity that looks like the original from database
"""
if not self.__original and self._original_pb:
self.__original = self.__class__._from_pb(self._original_pb)
return self.__original
@classmethod
def _from_pb(cls, pb, set_key=True, ent=None, key=None):
"""
save copy of original pb so we can track if anything changes between puts
"""
entity = super(ChangesetMixin, cls)._from_pb(pb, set_key=set_key, ent=ent, key=key)
if entity._original_pb is None and not entity._projection:
# _from_pb will get called if we unpickle a new object (like when passing through deferred library)
# so if we are being materialized from pb and we don't have a key, then we don't have _original
entity.__original = None
entity._original_pb = pb
return entity
Make a clone of the entity when you first read it:
and put it on the entity itself so it can be referenced later when desired. That way you don't have to do a second datastore read just to make the comparison
We override two different Model methods to make this happen:
@classmethod
def _post_get_hook(cls, key, future):
"""
clone this entity so we can track if anything changes between puts
NOTE: this only gets called after a ndb.Key.get() ... NOT when loaded from a Query
see _from_pb override below to understand the full picture
also note: this gets called after EVERY key.get()... regardless if NDB had cached it already
so that's why we're only doing the clone() if _original is not set...
"""
entity = future.get_result()
if entity is not None and entity._original is None:
entity._original = clone(entity)
@classmethod
def _from_pb(cls, pb, set_key=True, ent=None, key=None):
"""
clone this entity so we can track if anything changes between puts
this is one way to know when an object loads from a datastore QUERY
_post_get_hook only gets called on direct Key.get()
none of the documented hooks are called after query results
SEE: https://code.google.com/p/appengine-ndb-experiment/issues/detail?id=211
"""
entity = super(BaseModel, cls)._from_pb(pb, set_key=set_key, ent=ent, key=key)
if entity.key and entity._original is None:
# _from_pb will get called if we unpickle a new object (like when passing through deferred library)
# so if we are being materialized from pb and we don't have a key, then we don't have _original
entity._original = clone(entity)
return entity
这篇关于如何读取_pre_put_hook中的旧属性值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!