由于decorator实现,数据库模型上的串扰



我有一个在App Engine上运行的web服务器,它使用ndb进行数据存储。

数据模型看起来像这样:

@acl
class MyModel(ndb.Model): 
... 
access_control = ndb.JsonProperty(default={})

我使用@acl装饰器来用一些访问控制方法来扩充我的模型。装饰师看起来像这样:

def acl(model):
def grant(self, subject, credentials): 
logging.debug("ACL before: {}".format(self.access_control))
self.access_control[subject] = { ... }  # Set correct value.
logging.debug("ACL after: {}".format(self.access_control))
model.grant = grant
...
...

从我的应用程序来看,我希望这样称呼它:

>>> mdl = MyModel(...)
>>> mdl.grant("someone", "creds")
ACL before: {}
ACL after: { < properly populated access_control > }

但相反,我得到了类似的东西:

>>> mdl1 = MyModel(...)
>>> mdl1.grant("someone", "creds")
ACL before: {}
ACL after: { < properly populated access_control > }
>>> mdl2 = MyModel(...)
>>> mdl2.grant("someone else", "other creds")
ACL before: { < values from mdl1 > }
ACL after: { < values from mdl1 concatenated with mdl2 > }

这个错误让我怀疑grant()函数中的self是就像一个全局值一样,因为它在积累以前调用的数据,即使在不同的实例上执行这些调用时也是如此。

问题是:为什么我的模型会在它们之间泄露数据self在decorator的上下文中是否与self在类方法的上下文中相同

模型属性是静态元素。另请参阅在Python中定义类变量的正确方法。

类decorator的存在可能会干扰ndb内部的正常操作,而ndb内部通常会处理属性值的正确初始化。

我对类装饰器不够熟悉,所以我不确定是否/如何使用修改后的装饰器来解决这个问题。也许是这样(显式初始化属性)?

def acl(model):
def grant(self, subject, credentials): 
self.access_control = {}  # Always start with the default value
logging.debug("ACL before: {}".format(self.access_control))
self.access_control[subject] = { ... }  # Set correct value.
logging.debug("ACL after: {}".format(self.access_control))
model.grant = grant

我选择的实现类似功能的(IMHO更容易理解)解决方案是使用普通类继承而不是装饰器(但注意值的重置相同):

class ACLModel(ndb.Model):
access_control = ndb.JsonProperty(default={})
def grant(self, subject, credentials):
self.access_control = {}
self.access_control[subject] = { ... }  # Set correct value.
class MyModel(ACLModel):
...
# other specific properties

继承方法使我能够轻松地为多个模型使用相同的访问控制代码(自包含)。decorator方法将对可以使用它的模型提出额外的要求——它们都需要具有access_control属性——而不是自包含的。

最新更新