Skip to content

Commit

Permalink
Recursive restore with has_many/one through assocs (#441)
Browse files Browse the repository at this point in the history
The query to find deleted has_many or has_one through associations
was being generated incorrectly because of specifying the wrong
foreign key for the table. This change uses the has_one/has_many
model's primary key as the foreign key.
  • Loading branch information
emilong committed Jun 5, 2023
1 parent cb04a4d commit 894269f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 3 deletions.
9 changes: 7 additions & 2 deletions lib/paranoia.rb
Expand Up @@ -217,7 +217,12 @@ def restore_associated_records(recovery_window_range = nil)

if association_data.nil? && association.macro.to_s == "has_one"
association_class_name = association.klass.name
association_foreign_key = association.foreign_key

association_foreign_key = if association.options[:through].present?
association.klass.primary_key
else
association.foreign_key
end

if association.type
association_polymorphic_type = association.type
Expand All @@ -226,7 +231,7 @@ def restore_associated_records(recovery_window_range = nil)
association_find_conditions = { association_foreign_key => self.id }
end

association_class = association_class_name.constantize
association_class = association.klass
if association_class.paranoid?
association_class.only_deleted.where(association_find_conditions).first
.try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
Expand Down
66 changes: 65 additions & 1 deletion test/paranoia_test.rb
Expand Up @@ -49,7 +49,11 @@ def setup!
'active_column_model_with_uniqueness_validations' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
'paranoid_model_with_belongs_to_active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN, active_column_model_with_has_many_relationship_id INTEGER',
'active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
'without_default_scope_models' => 'deleted_at DATETIME'
'without_default_scope_models' => 'deleted_at DATETIME',
'paranoid_has_through_restore_parents' => 'deleted_at DATETIME',
'empty_paranoid_models' => 'deleted_at DATETIME',
'paranoid_has_one_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
'paranoid_has_many_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
}.each do |table_name, columns_as_sql_string|
ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})"
end
Expand Down Expand Up @@ -1063,6 +1067,40 @@ def test_restore_recursive_on_polymorphic_has_one_association
assert_equal 1, polymorphic.class.count
end

def test_recursive_restore_with_has_through_associations
parent = ParanoidHasThroughRestoreParent.create
one = EmptyParanoidModel.create
ParanoidHasOneThrough.create(
:paranoid_has_through_restore_parent => parent,
:empty_paranoid_model => one,
)
many = Array.new(3) do
many = EmptyParanoidModel.create
ParanoidHasManyThrough.create(
:paranoid_has_through_restore_parent => parent,
:empty_paranoid_model => many,
)

many
end

assert_equal true, parent.empty_paranoid_model.present?
assert_equal 3, parent.empty_paranoid_models.count

parent.destroy

assert_equal true, parent.empty_paranoid_model.reload.deleted?
assert_equal 0, parent.empty_paranoid_models.count

parent = ParanoidHasThroughRestoreParent.with_deleted.first
parent.restore(recursive: true)

assert_equal false, parent.empty_paranoid_model.deleted?
assert_equal one, parent.empty_paranoid_model
assert_equal 3, parent.empty_paranoid_models.count
assert_equal many, parent.empty_paranoid_models
end

# Ensure that we're checking parent_type when restoring
def test_missing_restore_recursive_on_polymorphic_has_one_association
parent = ParentModel.create
Expand Down Expand Up @@ -1563,3 +1601,29 @@ class ParanoidBelongsTo < ActiveRecord::Base
belongs_to :paranoid_has_one
end
end

class ParanoidHasThroughRestoreParent < ActiveRecord::Base
acts_as_paranoid

has_one :paranoid_has_one_through, dependent: :destroy
has_one :empty_paranoid_model, through: :paranoid_has_one_through, dependent: :destroy

has_many :paranoid_has_many_throughs, dependent: :destroy
has_many :empty_paranoid_models, through: :paranoid_has_many_throughs, dependent: :destroy
end

class EmptyParanoidModel < ActiveRecord::Base
acts_as_paranoid
end

class ParanoidHasOneThrough < ActiveRecord::Base
acts_as_paranoid
belongs_to :paranoid_has_through_restore_parent
belongs_to :empty_paranoid_model, dependent: :destroy
end

class ParanoidHasManyThrough < ActiveRecord::Base
acts_as_paranoid
belongs_to :paranoid_has_through_restore_parent
belongs_to :empty_paranoid_model, dependent: :destroy
end

0 comments on commit 894269f

Please sign in to comment.