2013-06-19 71 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()