2013-01-23 54 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。 –