2013-06-19 70 views
7

這裏的動態版本錯誤是一些示例代碼:SQLAlchemy的DELETE造成由具有兩個延遲加載和相同的關係

users_groups = Table('users_groups', Model.metadata, 
    Column('user_id', Integer, ForeignKey('users.id')), 
    Column('group_id', Integer, ForeignKey('groups.id')) 
) 

class User(Model): 
    __tablename__ = 'users' 
    id = Column(Integer, primary_key=True) 


class Group(Model): 
    __tablename__ = 'groups' 
    id = Column(Integer, primary_key=True) 

    users = relationship('User', secondary=users_groups, lazy='select', backref='groups') 
    users_dynamic = relationship('User', secondary=users_groups, lazy='dynamic') 

所以這裏所發生的是,如果你一堆的用戶添加到一個組像這樣:

g = Group() 
g.users = [User(), User(), User()] 
session.add(g) 
session.commit() 

,然後嘗試刪除該組

session.delete(g) 
session.commit() 

你會得到一些FO這個錯誤的rm:

DELETE statement on table 'users_groups' expected to delete 3 row(s); Only 0 were matched. 

刪除關係的第二個版本(在我的情況下動態的)解決了這個問題。我甚至不知道從哪裏開始理解爲什麼會發生這種情況。在很多情況下,我一直在使用SQLAlchemy模型中的兩種不同關係的版本,以便在給定情況下使用最合適的查詢策略。這是它第一次引起意外問題。

歡迎任何建議。

回答

9

Group.users和Group.users_dynamic關係都試圖協調組被刪除的事實,以及能夠管理它們引用的對象;一個關係成功,而第二個關係失敗,因爲關聯表中的行已被刪除。最直接的解決辦法是,以紀念所有但viewonly相同的關係之一:

class Group(Base): 
    __tablename__ = 'groups' 
    id = Column(Integer, primary_key=True) 

    users = relationship('User', secondary=users_groups, lazy='select', backref='groups') 
    users_dynamic = relationship('User', viewonly=True, secondary=users_groups, lazy='dynamic') 

如果你仍然想兼得的關係處理一定程度的突變,你需要認真做,因爲SQLAlchemy的不知道如何在同一時間在兩個關係的變化之間進行協調,因此,如果您在兩個關係上進行等效突變,這樣的衝突可能會繼續發生(如雙重插入等)。只是本身照顧「刪除」的問題,你也可以嘗試Group.users_dynamic設置爲passive_deletes = TRUE:

class Group(Base): 
    __tablename__ = 'groups' 
    id = Column(Integer, primary_key=True) 

    users = relationship('User', secondary=users_groups, lazy='select', backref='groups') 
    users_dynamic = relationship('User', passive_deletes=True, secondary=users_groups, lazy='dynamic') 
+0

如果我理解正確的話,將被動刪除標誌只會在組被刪除時阻止ORM嘗試刪除該特定集合?如果是這樣,那就好像是最無副作用的免費解決方案,謝謝。 –

+0

另一方面,SQLAlchemy是我用過的最好的ORM,也是社區中最好的開源python庫之一。所以謝謝你,你和你的貓很棒。 –

0

我只是添加另一種簡單的解決方法。

您可以刪除項目本身之前刪除集合:

>>> for user in group.users: 
     group.users.remove(user) 
>>> db.session.delete(group) 
>>> db.session.commit() 

另外,您還可以將其設置爲空列表:

>>> group.users = [] 
>>> db.session.commit() 
>>> db.session.delete(group) 
>>> db.session.commit()