You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When the table has a foreign key relationship defined on it, paranoia struggles when soft deletion is followed by hard deletion.
Consider these two table schemas, with one row in each where both acts_as_paranoid. We want to soft delete both rows, and then at a later time hard delete both rows.
parent:
id INTEGER,
deleted_at DATETIME
child:
id INTEGER,
foreign_id INTEGER,
deleted_at DATETIME,
FOREIGN KEY(foreign_id) REFERENCES parent(id)
The problems encountered differs depending on the dependent relationship defined on the has_one/belongs_to relationship defined in the model. They both have workarounds but it would be better if paranoia could handle these situations natively.
Scenario 1 - dependent: :nullify
The order in which the two records are deleted leads to different data.
If the parent is deleted first, then child.foreign_id is set to NULL
If the child is deleted first, then child.foreign_id is NOT set to NULL
If this sequence of events happens:
child is soft-deleted
parent is soft-deleted (OPTIONAL STEP - makes no difference)
parent is hard-deleted
...then the deletion fails because the ID of the parent still exists in child.foreign_id.
The workaround is to soft-delete the child first, this means there is no longer a foreign key relationship and thus the parent can be deleted.
Alternatively, hard-deleting the child before hard-deleting the parent works, for the same reason.
Reproduction:
# in setup!'paranoid_model_with_foreign_key_selves'=>'self_parent_id INTEGER, deleted_at DATETIME, FOREIGN KEY(self_parent_id) REFERENCES paranoid_model_with_foreign_key_selves(id)',
# testdeftest_destroy_self_foreign_keyself_parent=ParanoidModelWithForeignKeySelf.createself_child=ParanoidModelWithForeignKeySelf.create(self_parent:)self_child.destroyself_parent.destroyself_parent.reload.really_destroy!self_child.reload.really_destroy!assert_equal0,ParanoidModelWithForeignKeySelf.with_deleted.countend# model setupclassParanoidModelWithForeignKeySelf < ActiveRecord::Baseacts_as_paranoidbelongs_to:self_parent,:class_name=>"ParanoidModelWithForeignKeySelf",:foreign_key=>:self_parent_id,optional: truehas_many:self_children,:class_name=>"ParanoidModelWithForeignKeySelf",:foreign_key=>:self_parent_id,dependent: :nullifyend
Scenario 2 - dependent: :destroy
In this scenario, it doesn't matter which way round you soft-delete the two records, the data is the same. The foreign key remains populated.
The problem then comes if you try and delete the child record. This throws a ActiveRecord::RecordNotFound error as it tries to find a matching parent record. There is one, but as it is soft-deleted it is not found by the paranoia code.
This can be reproduced in the paranoia test suite by amending this line in the setup! method:
When the table has a foreign key relationship defined on it, paranoia struggles when soft deletion is followed by hard deletion.
Consider these two table schemas, with one row in each where both
acts_as_paranoid
. We want to soft delete both rows, and then at a later time hard delete both rows.The problems encountered differs depending on the
dependent
relationship defined on thehas_one/belongs_to
relationship defined in the model. They both have workarounds but it would be better if paranoia could handle these situations natively.Scenario 1 - dependent: :nullify
The order in which the two records are deleted leads to different data.
child.foreign_id
is set toNULL
child.foreign_id
is NOT set toNULL
If this sequence of events happens:
...then the deletion fails because the ID of the parent still exists in
child.foreign_id
.The workaround is to soft-delete the child first, this means there is no longer a foreign key relationship and thus the parent can be deleted.
Alternatively, hard-deleting the child before hard-deleting the parent works, for the same reason.
Reproduction:
Scenario 2 - dependent: :destroy
In this scenario, it doesn't matter which way round you soft-delete the two records, the data is the same. The foreign key remains populated.
The problem then comes if you try and delete the child record. This throws a
ActiveRecord::RecordNotFound
error as it tries to find a matching parent record. There is one, but as it is soft-deleted it is not found by the paranoia code.This can be reproduced in the paranoia test suite by amending this line in the
setup!
method:Then adding this test:
When running
child.reload.really_destroy
it throws:The workaround is to
really_destroy
the parent first.The text was updated successfully, but these errors were encountered: