关于python:由于装饰器实现而在数据库模型上进行交谈

Cross talk on database models because of decorator implementation

我有一个运行应用程序引擎的Web服务器,它使用ndb来存储数据。

数据模型如下所示:

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

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

1
2
3
4
5
6
7
8
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
    ...
...

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

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

但是我得到了类似的东西:

1
2
3
4
5
6
7
8
9
>>> 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在修饰器的上下文中,与self在类方法的上下文中相同吗?


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

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

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

1
2
3
4
5
6
7
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更容易理解)解决方案是使用纯类继承而不是修饰器(但请注意,值的重置相同):

1
2
3
4
5
6
7
8
9
10
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属性——而不是独立的。