我在App Engine上运行了一个使用ndb
进行数据存储的Web服务器。
数据模型如下所示:
@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 > }
这个错误让我怀疑self
函数中的grant()
是以某种方式
表现得像一个全球价值,因为它正在累积先前电话的数据,
即使这些调用是在不同的实例上执行的。
问题是:为什么我的模型会在它们之间传播数据?
self
在装饰器的上下文中,与类方法上下文中的self
相同吗?
答案 0 :(得分:1)
模型属性是静态元素。另请参阅correct way to define class variables in Python。
类装饰器的存在可能会干扰它们从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
我选择实现类似功能的(恕我直言更容易理解)解决方案是使用普通类继承而不是装饰器(但注意同样重置值):
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
继承方法允许我轻松地为多个模型使用相同的访问控制代码(自包含)。装饰器方法会对可以使用它的模型提出额外的要求 - 它们都需要具有access_control
属性 - 而不是自包含。