简体   繁体   English

在SQLAlchemy中停止所有级联删除

[英]Stop all on delete cascading in SQLAlchemy

Trying to wrap my head around the following problem: 试图解决以下问题:

I have three classes, A , AB and B , as so: 我分为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'

Essentially, this is a m2m relationship between A and B . 本质上,这是AB之间的m2m关系。 The only non-standard thing is that there is a column id in the table AB . 唯一不标准的事情是表AB中有一个列id It's there for a reason. 在那里是有原因的。

I want to implement "merging" of two instances of A . 我想实现A的两个实例的“合并”。 We are given a1 and a2 . 给我们a1a2 Then, before deleting a1 , all of its relationships with AB s should be reassigned to a2 . 然后,在删除a1之前,应将其与AB的所有关系重新分配给a2 It is vitally important to retain the values of AB.id in the process (hence, no new instances of AB should actually be created or deleted). 在此过程中保留AB.id的值至关重要(因此,实际上不应创建或删除AB新实例)。

THE PROBLEM No matter how I try, every time I delete an instance of A , SQLAlchemy attempts to update the foreign_key with a NULL value, consequently breaking the NOT NULL constraint. 问题无论如何尝试,每次删除A的实例时,SQLAlchemy都会尝试使用NULL值更新foreign_key,从而打破NOT NULL约束。 It does that by issuing an explicit UPDATE ab SET a_id = NULL WHERE id = ... . 它通过发出显式UPDATE ab SET a_id = NULL WHERE id = ... It does so, even though I have the following loop in my program: 即使我的程序中存在以下循环,它也会这样做:

for ab in a1.abs:
    ab.a_id = a2.id
    session.db.add(ab)

session.db.delete(a1)

Therefore it would seem to me that before delete is issued, all the ab s related to a1 were safely moved to a2 , however something is wrong. 因此,在我看来,在发出删除操作之前,所有与a1相关的ab都已安全地移至a2 ,但是出了点问题。

SOME NON-SOLUTIONS 一些非解决方案

  • The passive_deletes flag. passive_deletes标志。 The difference in behaviour concerns only those rows that aren't already in the memory which is not good. 行为上的差异仅涉及内存中尚未存在的那些行,这不是很好。
  • Adding a delete cascade is very risky for me. 添加delete级联对我来说非常冒险。 I want to really make sure that no ab objects are lost in the process of merging. 我要真正确保合并过程中不会丢失任何ab对象。
  • Updating the lists kept in the associations manually, doesn't seem to have any effect whatsoever (The UPDATE is issued again). 手动更新关联中保留的列表似乎没有任何效果(再次发出UPDATE )。

Would very much appreciate your help! 非常感谢您的帮助!

Whenever there are problems with synchronisation between the contents of the relationships and the actual set of rows in the database, I recommend using session.expire() . 每当关系的内容与数据库中的实际行集之间的同步出现问题时,我建议使用session.expire() It forces values being reloaded from the DB the next time data is accessed. 下次访问数据时,它将强制从数据库重新加载值。

About expire & refresh: 关于过期和刷新:

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

About stale data in collections: 关于集合中的陈旧数据:

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

These functions saved my life. 这些功能挽救了我的生命。 Now, everything works as expected. 现在,一切正常。 Moreover, I can do bulk updates via sqlalchemy.sql.expression API which is generally much faster without sacrificing data integrity in the ORM layer. 此外,我可以通过sqlalchemy.sql.expression API进行批量更新,该API通常更快,而不会牺牲ORM层中的数据完整性。

Hmmm... something is very wrong here. 嗯...这里有些问题。

"UPDATE ab SET a_id = NULL WHERE id = a1.id" should affect 0 rows since you have already updated all ab where ab.a_id = a1.id. “更新ab SET a_id = NULL,其中id = a1.id”应该影响0行,因为您已经更新了所有ab,其中ab.a_id =a1.id。

Therefore only one possible explanation, you have failed to update ab. 因此,仅一种可能的解释,您没有更新ab。 Computers tend not to lie. 电脑往往不会撒谎。 We only miss their logic sometimes. 有时我们只会错过他们的逻辑。

My solution: to make sure everyhting is ok, before issuing the delete, find all rows of ab that point to a1.id if as it is to be expected, you find at least one, try to understand why that row is still there. 我的解决方案:为确保一切正常,在发出删除之前,找到所有指向a1.id的ab行,如果可以预期的话,您发现至少一个行,请尝试理解为什么该行仍然存在。

maybe session.db.add(ab) can be replaced by somehting like session.db.update(ab) and the add() is adding new rows to your ab table, or if no update exist or you can't use it, then delete first all ab where ab.a_id = a1.id after making sure you have correctly made copies (which I think you have) 也许session.db.add(ab)可以替换为session.db.update(ab)之类的东西,并且add()向ab表中添加新行,或者如果不存在更新或您无法使用它,然后先确定您已正确制作副本(我认为您拥有),然后首先删除其中ab.a_id = a1.id的所有ab

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM