[英]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: 我分为
A
, AB
和B
三个类:
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
. 本质上,这是
A
和B
之间的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
. 给我们
a1
和a2
。 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 一些非解决方案
passive_deletes
flag. passive_deletes
标志。 The difference in behaviour concerns only those rows that aren't already in the memory which is not good. 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
对象。 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.