因装饰器实现而在数据库模型上进行交叉讨论

时间:2017-10-19 19:00:05

标签: python app-engine-ndb python-decorators

我在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相同吗?

1 个答案:

答案 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属性 - 而不是自包含。