2017-10-19 10 views
1

我有一个在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 > } 

这个错误让我怀疑selfgrant()函数是以某种方式 作用像一个全局值,因为它正在积累以前呼叫的数据,即使在不同实例上执行这些呼叫时也是如此。

问题是:为什么我的模型在它们之间泄漏数据? self在装饰器的上下文中,与类方法的上下文中的self相同?

回答

0

模型属性是静态元素。另见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属性 - 不是自包含的。