2017-10-19 138 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屬性 - 不是自包含的。