在SQLAlchemy中停止所有删除级联

时间:2013-08-27 15:00:10

标签: python sql orm sqlalchemy cascade

试图解决以下问题:

我有三个课程,AABB,因此:

class AB(Base):
    id = Column(Integer, primary_key=True)
    a_id = Column(Integer, ForeignKey('a.id'), nullable=False)
    a = relationship(
        'A', 
        cascade='save-update', 
        backref=backref(
            'abs', 
            cascade='save-update', 
            uselist=True
        )
    )
    b_id = Column(Integer, ForeignKey('b.id'), nullable=False)
    b = relationship(
        'B', 
        cascade='save-update', 
        backref=backref(
            'abs', 
            cascade='save-update', 
            uselist=True
        )
    )
    __tablename__ = 'ab'

class A(Base)
    id = Column(Integer, primary_key=True)
    __tablename__ = 'a'

class B(Base)
    id = Column(Integer, primary_key=True)
    __tablename__ = 'b'

基本上,这是AB之间的m2m关系。唯一不标准的是表id中有一列AB。这是有原因的。

我想实现A的两个实例的“合并”。我们获得a1a2。然后,在删除a1之前,应将其与AB的所有关系重新分配给a2。在过程中保留AB.id的值非常重要(因此,实际上不应创建或删除AB的新实例)。

问题无论我如何尝试,每次删除A的实例时,SQLAlchemy都会尝试使用NULL值更新foreign_key,从而打破{ {1}}约束。它通过发布明确的NOT NULL来做到这一点。它会这样做,即使我的程序中有以下循环:

UPDATE ab SET a_id = NULL WHERE id = ...

因此,在我发现删除发布之前,与for ab in a1.abs: ab.a_id = a2.id session.db.add(ab) session.db.delete(a1) 相关的所有ab都已安全地移至a1,但是出现了问题。

一些非解决方案

  • a2标志。行为上的差异只涉及那些不在内存中但不好的行。
  • 添加passive_deletes级联对我来说非常危险。我想确保在合并过程中没有丢失delete个对象。
  • 手动更新关联中保存的列表似乎没有任何效果(再次发出ab)。

非常感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

每当关系内容与数据库中的实际行集之间存在同步问题时,我建议使用session.expire()。它会强制在下次访问数据时从数据库重新加载值。

关于过期&刷新:

http://docs.sqlalchemy.org/en/latest/orm/session.html#refreshing-expiring

关于集合中的陈旧数据:

http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#deleting-from-collections

这些功能挽救了我的生命。现在,一切都按预期工作。此外,我可以通过sqlalchemy.sql.expression API进行批量更新,这通常会快得多,而不会牺牲ORM层中的数据完整性。

答案 1 :(得分:0)

嗯......这里有些不对劲。

“UPDATE ab SET a_id = NULL WHERE id = a1.id”应该影响0行,因为你已经更新了所有ab,其中ab.a_id = a1.id。

因此只有一种可能的解释,您无法更新ab。电脑往往不撒谎。我们有时只会错过他们的逻辑。

我的解决方案:为了确保每一个都没问题,在发出删除之前,找到所有指向a1.id的ab行,如果它是预期的,你找到至少一个,试着理解为什么那行是还在那里。

也许session.db.add(ab)可以替换为session.db.update(ab)之类的东西,而add()正在向你的ab表添加新行,或者如果不存在更新或者你不能使用它,然后在确保你已经正确制作副本(我认为你有)后首先删除所有ab ab.a_id = a1.id