ActsAsParanoid / acts_as_paranoid

ActiveRecord plugin allowing you to hide and restore records without actually deleting them.
MIT License
1.45k stars 190 forks source link

Destroying fully doesn't honor dependent: :destroy, causing foreign key violation #142

Open kirykr opened 4 years ago

kirykr commented 4 years ago
class User < ActiveRecord::Base
  acts_as_paranoid
  has_many :user_permissions, -> { order_by_program_name }, dependent: :destroy
end

class Permission < ActiveRecord::Base
  acts_as_paranoid
  has_many   :user_permissions, dependent: :destroy
end

class UserPermission < ActiveRecord::Base
  acts_as_paranoid

  belongs_to :user
  belongs_to :permission
end

@user = User.find(1)
@user.destroy_fully!

ActiveRecord::InvalidForeignKey (PG::ForeignKeyViolation: ERROR:  update or delete on table "users" violates foreign key constraint "fk_rails_11302c83e2" on table "user_permissions"
DETAIL:  Key (id)=(44) is still referenced from table "user_permissions".
: DELETE FROM "users" WHERE "users"."id" = $1):
  app/controllers/users_controller.rb:70:in `destroy'

Is there anythinng wrong here? Thanks

mvz commented 4 years ago

@kirykr just to double check, you would want:

Correct?

kirykr commented 4 years ago

@mvz Yes , you're correct.

szechyjs commented 4 years ago

I have a similar issue...

class Parent < ActiveRecord::Base
  has_many :children, dependent: :destroy
end

class Child < ActiveRecord::Base
  acts_as_paranoid

  belongs_to :parent
end

Parent.first.destroy

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:  update or delete on table "parents" violates foreign key constraint "fk_rails_b38590a748" on table "children"
DETAIL:  Key (id)=(aa67c98c-d81f-5a9c-b0bc-26caa0051aea) is still referenced from table "children".

Since Parent is not paranoid I would expect calling #destroy to work and fully destroy its children.

mvz commented 4 years ago

Hi @szechyjs your case is different enough that it should be in a separate issue. Can you open one please?

Genkilabs commented 3 months ago

Just checking in if there will be any motion on this or if it's abandoned?

Genkilabs commented 3 months ago

I'm thinking of doing something like this in the mean time... thoughts?

class ApplicationRecord < ActiveRecord::Base
    # override fully_destroy! to destroy all associated objects with dependent: :destroy
    # @returns [Boolean] true if destroyed
    def destroy_fully_with_dependencies!
        dependent_associations = self.class.reflect_on_all_associations(:has_many).select { |a| a.options[:dependent] == :destroy }
        dependent_associations.each do |association|
            self.send(association.name).each(&:destroy_fully_with_dependencies!)
        end
        self.destroy_fully!
    end
mvz commented 2 months ago

@Genkilabs that should work. Be sure to create some tests for it!