2013-01-23 57 views
8

更新:SQLAlchemy的雙向關係協會代理

對於有這個問題的人,與very latest SQLAlchemy這一行爲已得到修復。

原來的問題:

我有越來越協會代理正確更新的問題。

使用示例模型這裏:http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/associationproxy.html#simplifying-association-objects

但隨着該行改變UserKeyword:

keyword = relationship("Keyword", backref=backref("user_keywords", cascade="all, delete-orphan")) 

並將其加入到關鍵字:

users = association_proxy('user_keywords', 'user') 

所以關鍵字實例有名單用戶。如預期

以下工作:

>>> rory = User("rory") 
>>> session.add(rory) 
>>> chicken = Keyword('chicken') 
>>> session.add(chicken) 
>>> rory.keywords.append(chicken) 
>>> chicken.users 
[<__main__.User object at 0x1f1c0d0>] 
>>> chicken.user_keywords 
[<__main__.UserKeyword object at 0x1f1c450>] 

但清除做奇怪的事情。從關聯代理列表中刪除,如下所示:

>>> rory.keywords.remove(chicken) 

由於SA試圖將其中一個外鍵列設置爲null,因此會導致完整性錯誤。

這樣做:

>>> rory.user_keywords.remove(rory.user_keywords[0]) 

結果在此:

>>> chicken.users 
[None] 

我錯過了一些東西很明顯,不是嗎?

回答

7

UserKeyword要求它同時與KeywordUser相關聯以便保持。當您將它與UserKeyword相關聯,但將其從User.user_keywords集合中刪除時,它仍然與Keyword關聯。

>>> rory.keywords.remove(chicken) 

# empty as we expect 
>>> rory.user_keywords 
[] 

# but the other side, still populated. UserKeyword 
# has no User, but still has Keyword 
>>> chicken.user_keywords 
[<__main__.UserKeyword object at 0x101748d10>] 

# but the User on that UserKeyword is None 
>>> chicken.user_keywords[0].user is None 
True 

# hence accessing the "association" gives us None 
# as well 
>>> chicken.users 
[None] 

所以,如果我們現在刷新()這個權利,你已經有了一個UserKeyword對象準備去,但對其沒有User,所以你得到的是NULL錯誤。在INSERT時間,該對象不被視爲「孤兒」,除非它不與任何Keyword.user_keywordsUser.user_keywords收藏關聯。只有在你說的del chicken.user_keywords[0]或同等數字時,纔會看到沒有生成INSERT,並且忘記了UserKeyword對象。

如果您在將對象從「rory」中刪除之前將對象刷新到數據庫,則情況會發生變化。 UserKeyword現在是持久的,當你從「rory」中刪除「雞」。關鍵字」,‘刪除孤兒’事件打完這刪除UserKeyword,即使它仍然與Keyword對象相關聯:

rory.keywords.append(chicken) 

session.flush() 

rory.keywords.remove(chicken) 

session.flush() 

看到SQL:

INSERT INTO "user" (name) VALUES (%(name)s) RETURNING "user".id 
{'name': 'rory'} 

INSERT INTO keyword (keyword) VALUES (%(keyword)s) RETURNING keyword.id 
{'keyword': 'chicken'} 

INSERT INTO user_keyword (user_id, keyword_id, special_key) VALUES (%(user_id)s, %(keyword_id)s, %(special_key)s) 
{'keyword_id': 1, 'special_key': None, 'user_id': 1} 

DELETE FROM user_keyword WHERE user_keyword.user_id = %(user_id)s AND user_keyword.keyword_id = %(keyword_id)s 
{'keyword_id': 1, 'user_id': 1} 

現在一個合理的人會問,「這不是不一致嗎?」而此刻我會說,「絕對」。我需要查看測試用例,看看這種行爲差異的基本原理,我們已經在代碼中確定了爲什麼它會以這種方式出現,我很確定這個「孤兒」是如何區分的爲了「等待」而不是「持久」對象是故意的,但是在這個特殊的排列中顯然產生了一個奇怪的結果。如果我能找到一個可行的方案,我可以在0.8中進行修改。

編輯:http://www.sqlalchemy.org/trac/ticket/2655總結了我將不得不考慮的問題。這種行爲有一個具體的測試,需要追溯到它的起源。

+0

謝謝堆zzzeek。 –