krainboltgreene
krainboltgreene

Reputation: 1861

How do I find out why I couldn't #destroy() a record?

person = Person.find(4123)
person.destroy #=> false

What ways do I have to find out why the record wasn't deleted? The model has two validations, both trigger on create only. It has one callback, but the callback doesn't block if it fails.

I have no traceback or errors to look at.

Upvotes: 54

Views: 20054

Answers (6)

Mike State
Mike State

Reputation: 539

I've run into this a few times now, and have finally come across a simple means of identifying the reason the record was not destroyed (tested in Rails 5.x).

Simply wrap the call to destroy! in a rescue block, and look at error.record.errors.

begin
  person = Person.find(4123)
  person.destroy! #=> Note the exclamation mark which will cause an error if it fails
rescue ActiveRecord::RecordNotDestroyed => error
  puts "errors that prevented destruction: #{error.record.errors.full_messages}"
end

Upvotes: 37

Berti92
Berti92

Reputation: 471

Had the exact same error right now. I didn't get any backtrace, because it was a logic error.

In my case my models had a conflicting dependent: destroy in the relations in the model. That means my model wanted to delete a parent model which was already deleted before hand.

I think it's a bit confusing, i'll make it clear by declaring the models:

class Section < ApplicationRecord
  has_many :contents, dependent: :destroy

class Content < ApplicationRecord
  belongs_to :section
  has_many :quizzes, dependent: :destroy

class Quiz < ApplicationRecord
  belongs_to :content, dependent: :destroy

I deleted the Section section.destroy and got the error. The error was the dependent: destroy in the relation :content in the Quiz-model, because the content was already deleted by the dependent: destroy in the section model. I hope this will help to find the problem :)

Upvotes: 4

pdobb
pdobb

Reputation: 18037

Update: See Mike Slate's answer for a quicker solution: https://stackoverflow.com/a/53872915/171183.

I ran into this same issue and here's what I've done to figure out what's going on...

(TL;DR: Complete code listing given at bottom.)

First, for the class of the object I'm trying to destroy, I ran this to figure out what all associations are set as dependent: :destroy:

ary = 
  <MyClass>.reflect_on_all_associations.select { |a| 
    a.options[:dependent] == :destroy 
  }.map(&:name)

Then I called each of the associations named in ary on my object and collected the results. This limits the association names to only those that actually have dependent objects:

ary.select! { |association_name| 
  <my_object>.send(association_name).present? 
}

Then I can attempt to destroy each of the objects returned by these association names to find the problem object(s):

associated_objects = 
  ary.each_with_object([]) { |association_name, acc| 
    acc.concat(<my_object>.send(association_name))
  }

problem_objects =
  associated_objects.select { |obj| obj.destroy; obj.errors.any? }
# ...

I then could look at the errors on each problem object:

problem_objects.map(&:errors)

And that's where I finally saw the error that was causing the destroy to fail. From there it was a Simple Matter Of Programming (SMOP) to fix the issue.

In my case, there was a before_destroy callback preventing destroy from working on one of my dependent object associations. To make this simpler to debug in the future, I've decided to start logging an error to the Rails log on failed callbacks (in addition to adding the error message to errors.base).

Complete code listing:

my_object = <your_object_here>

ary = 
  my_object.class.reflect_on_all_associations.select { |a| 
    a.options[:dependent] == :destroy 
  }.map(&:name)

ary.select! { |association_name| my_object.send(association_name).present? }

associated_objects = 
  ary.flat_map { |association_name| my_object.send(association_name) }

problem_objects =
  associated_objects.select { |obj| obj.destroy; obj.errors.any? }

problem_objects.map(&:errors)

Upvotes: 48

YWCA Hello
YWCA Hello

Reputation: 3059

This error is usually raised due to a dependent: :restrict_with_error specified on an association in the model. Run a quick search for this in your model definition (as well as associated model definitions) and see if that's the case.

Upvotes: 5

Edward
Edward

Reputation: 1914

You should add some debug information e.g. logger.debug and look at your log files. e.g. log/production.log if running in production mode and etc and you should be able to find out why. If not, you can post the relevant log file section (e.g. the action you are running) up here and we can help you out.

Upvotes: 0

zeantsoi
zeantsoi

Reputation: 26203

What version Rails are you on? In Rails 4, you can force a destroy using person.destroy!. If the action fails, a traceback will be displayed.

Upvotes: 2

Related Questions