Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't destroy parent if not paranoid #151

Open
szechyjs opened this issue Feb 21, 2020 · 5 comments
Open

Can't destroy parent if not paranoid #151

szechyjs opened this issue Feb 21, 2020 · 5 comments
Assignees

Comments

@szechyjs
Copy link

If a parent model isn't paranoid it's impossible to destroy it. It calls #destroy on all the children, which doesn't actually delete them, then when it goes to delete the parent, it gets a foreign key violation from the database.

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.

If I add acts_as_paranoid to the parent it works just fine, however I have no need to soft delete the parent.

Originally posted by @szechyjs in #142 (comment)

@mvz
Copy link
Contributor

mvz commented Feb 22, 2020

This behavior is as intended. ActsAsParanoid aims to never allow accidental full destruction of a record. It cannot know that the destruction of Child objects is fine for you in this case.

The simplest solution for you would be to explicitly destroy the chidren with #destroy_fully before calling #destroy on the parent. This requires no change to ActsAsParanoid.

Going a bit further, I think there are three behaviors involving deletion of the Child that would be possible for a has_many/belongs_to relation (I'm leaving out the other options like nullify and restrict_with_error):

  1. Fully destroy all children. This is what you're trying to achieve with dependent: :destroy.
  2. Soft-delete all children. This is what actually happens with dependent: :destroy, but it's broken for your case since the foreign key is not nullified.
  3. Delete the children from the database without any callbacks. This happens with dependent: :delete_all and may work for you depending on other factors.

It may be possible to add a mechanism to make case 1 work by adding some dependent: :destroy_fully option. Changing case 2 so that it works for all use cases is worth investigating but seems tricky.

@szechyjs
Copy link
Author

I agree that case 1 would probably be the best fit here, as it would be opt-in (dependent: :destroy_fully).

@diegotc86
Copy link

Hi, I've been struggling with exactly this problem for a while. I finally solved it this way:

# Parent model
before_destroy do
    children.with_deleted.each(&:destroy_fully!)
end

You can leave the option dependent: :destroy or remove it. If you leave it, it first will make a soft delete on every child and then the before_destroy block will delete permanently the children.

Hope this is helpful for someone out there.

@mvz mvz self-assigned this Nov 25, 2020
@Atashia-Sameen

This comment was marked as resolved.

@mvz
Copy link
Contributor

mvz commented Nov 1, 2024

Hi @Atashia-Sameen that looks like a different problem since you're using dependent: :nullify inside Parent, instead of dependent: :destroy. Could you please open a new issue for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants