2012-10-30 67 views
1

我有一種'客戶'。我想運行一個事務,在將要插入新的「客戶」時鎖定整個Kind。交易將首先查詢以檢查新的「客戶」名稱是否已經存在,然後如果沒有找到匹配,則交易的第二部分運行插入。這樣我強制執行一個唯一約束(也限制每秒大約1次插入操作)。谷歌App Engine分配實體組/父鍵,唯一約束

我不滿意的解決方案是讓所有「客戶」實體都在同一個實體組中,然後創建一個名爲「EntityGroups」的Kind,其中有一個名爲「CustomersGroup」的記錄。這個記錄每次被用作新創建的「客戶」實體的父項,從而將整個種類分組爲一個實體組。

我的問題是:我很擔心使用像'CustomerGroup'這樣的幻影記錄,因爲如果發生任何事情並且丟失或刪除了,我不能將任何新的'客戶'實體分配給同一個組!我想,將每個「客戶」實體的父母分配給一個靜態任意父母,比如'1111111'會更好嗎?我認爲術語是「虛擬根實體」,我該怎麼做?

請幫助任何關於我如何最好地處理這個問題的建議!然後,你實例

class CustomerParent(ndb.Model): 
    pass 

和存儲您的母公司:

回答

0

您可以創建一個父實體,像這樣

customers_parent = CustomerParent() 
customers_parent.put() 

最後,當你創建你所有的客戶的實體,您指定親本:

a_customer = Customer(parent=customers_parent.key, ...) 
a_customer.put() 

希望這會有所幫助!

+0

你不想走單親父母的路線。這大大限制了你的表現。 – dragonx

+0

感謝Thanos,這正是我正在做的。但是我擔心如果我的主人「CustomerParent」實體被刪除,或者意外創建了另一個條目。然後我無法將新客戶添加到原始實體組,或者新客戶被添加到新實體組中! – GrantsV

+0

感謝Dragonx,但這種性能限制並不是問題。什麼是節目製作人正在創建2個具有相同名稱字段的實體。而且我不能使用KEY_NAME來強制執行,因爲名稱字段可能會及時更改。 – GrantsV

3

爲什麼不使用:NDB的get_or_insert:事務性地檢索現有實體或創建一個新實體。

https://developers.google.com/appengine/docs/python/ndb/modelclass#Model_get_or_insert

+0

謝謝Voscausa。假設我有一個名爲ACME的客戶。我查詢他並沒有匹配。我開始對ACME進行放置操作,就像另一個用戶開始將他們的ACME輸入到db一樣。當業務邏輯規定不允許重複時,我們最終會在數據存儲中使用2個ACME。我不能將ACME作爲KEY_NAME存儲(其中get_or_insert是解決方案,並且唯一性是有保證的),因爲業務邏輯規定公司名稱字段可能隨時間而改變! – GrantsV

+0

然後爲公司指定一個唯一的ID,並將其用作數據存儲中的ID。這永遠不會改變,即使公司名稱的確如此。然後它只是一個get_by_id來獲取當前公司名稱。正如答案所暗示的那樣,使用get_or_insert正是這裏所做的事情!做吧! :) –

3

CustomerGroup記錄不需要存在爲它作爲父母採取行動。只需手工創建密鑰並將其分配爲相關記錄的父代。

如果它不存在,您不必擔心它被刪除!

當您創建模型並將另一個設置爲父級時,系統不會檢查(也不需要)該模型實際上存在。

因此,例如:

rev_key = ndb.Key('CustomerGroup', '11111', 'Customer', 'New_Customer_Name') 

然而,隨着的關鍵模型:('CustomerGroup', '11111')實際上並不存在,但它仍然可以在祖先鏈。

+0

真棒保羅!那正是我正在尋找的! – GrantsV

1

GrantsV,您可以通過爲每個唯一約束創建一個代理實體並使用跨組事務來提交正常寫入的約束來實現此目的。

class UniqueConstraint(db.Model): 
    # Consider adding a reference to the owner of the constraint. 
    @db.transactional(propagation=db.MANDATORY, xg=True) 
    @classmethod 
    def reserve(cls, kind, property, value): 
    key = cls.__get_key(kind, property, value) 
    if db.get(key): 
     raise Exception # Already exists 
    cls(key=key).put() 

    @db.transactional(propagation=db.MANDATORY, xg=True) 
    @classmethod 
    def release(cls, kind, property, value): 
    db.delete(cls.__get_key(kind, property, value)) 

    @classmethod 
    def __get_key(cls, kind, property, value): 
    # Consider using a larger entity group. 
    return db.Key.from_path(cls.kind(), '%s:%s:%s' % (kind, property, value)) 
    # To restrict to 1 insert per second per kind, use: 
    # return db.Key.from_path(cls.kind(), kind, cls.kind(), '%s:%s' % (property, value))