is there any way, given certain condition, cancel the destroy of an object on the before_destroy callback of active record? Thanks
5 Answers
You should return false
.
Rails 5
"Canceling callbacks
If a before_* callback throws :abort, all the later callbacks and the associated action are cancelled."
Rails 4 and lower
"Canceling callbacks
If a before_* callback returns false, all the later callbacks and the associated action are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks defined as methods on the model, which are called last."

- 1
- 1

- 6,993
- 2
- 41
- 60
-
9With Rails 5, returning false no longer works. One should `throw(:abort)` to prevent record deletion. See [this comment](http://stackoverflow.com/questions/123078/how-do-i-validate-on-destroy-in-rails#comment59333149_123190) – RFVoltolini Aug 08 '16 at 15:16
-
1Thanks for specifying the :abort technique for Rails 5. Should really help me out. – Nov 28 '16 at 15:55
-
I've searched forever to find this answer - it's not even clear from the official documentation http://api.rubyonrails.org/classes/ActiveRecord/RecordNotDestroyed.html - thanks! – mtrolle Jun 09 '17 at 13:58
-
For rails 3, returning false just skips the other callbacks but it destroys the actual object. Anyone? – Balaji Radhakrishnan Nov 21 '18 at 03:11
As none of the given answers really solves the problem, but the comment above tells it - here the in form of an answer to make it easy to find:
In rails 5, instead of
before_destroy do
if self.some_condition?
return false
end
end
use
before_destroy do
if self.some_condition?
throw(:abort)
end
end
to make sure destroy is not being perfomed.
thanks to RFVoltolini's comment - this saved my day!

- 495
- 3
- 13
Rails wraps saves and destroys in a transaction, so a raise
in the callback would work:
class Post < ActiveRecord::Base
before_destroy :saveable?
def saveable?
if true
raise "Destroy aborted; you can't do that!"
end
end
end
Substitute true
for your condition.
Here's the abridged console output:
[1] pry(main)> Post.first.id
=> 1
[2] pry(main)> Post.first.destroy
RuntimeError: Destroy aborted; you can't do that!
[3] pry(main)> Post.first.id
=> 1

- 5,490
- 1
- 25
- 35
-
-
Makes sense. Because this is a `before_*` callback, either `raise` or `false` works. The benefit of a `raise` is that you'll get a clear message you can trace in the logs, rather than the `destroy` quietly not happening. Which to choose depends on how unexpected your failure case may be. – Jake Worth Jun 14 '16 at 20:27
-
1In Rails 5, you will have to explicitly `raise :abort`. You might as well start now. – mwoods79 Jun 15 '16 at 02:19
-
2In Rails 5, `raise :abort` doesn't work for me. I think it *has* to be `throw :abort` – Maiko Kuppe Nov 25 '16 at 06:45
Returning false
is the way to do it properly:
before_destroy do
if self.some_condition?
return false
end
end
You can see the documentation here under point 6 Halting Execution
.
http://guides.rubyonrails.org/active_record_callbacks.html

- 1,921
- 2
- 16
- 26
You can also override the #destroy
method:
def destroy
study_assignments.empty? ? super : raise("can not be destroyed")
end

- 19,188
- 9
- 91
- 111