[Proposal] Add `#destroy_all!` method to `ActiveRecord::Relation`

Sammary

Add #destroy_all! to ActiveRecord and performs deletion processing that raises an exception if it fails.

Background

There is Already have destroy_all, but there are scenes where I want to raise an exception if it fails, for example when I want to control transactions.

I also want destroy_by! for the same reason, but I don’t have it now. After the destroy_all! method has been added, we can use it to add destroy_by!

Proposal

If the before_destroy callback throws abort the action is cancelled and destroy_all! raises ActiveRecord::RecordNotDestroyed.

It is not necessary to execute rollback processing. If there is more than one target record and you want to ensure that all records are deleted, you should realize this by explicitly using transaction to cause rollback.

class User < ApplicationRecord
  before_destroy do
     throw :abort if id == 3
   end
end

ActiveRecord::Base.transaction do 
  User.where(id: 1..3).destroy_all!  
end  
 
=>  TRANSACTION (1.5ms)  BEGIN
  user Load (1.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` BETWEEN 1 AND 3
  user Destroy (1.2ms)  DELETE FROM `users` WHERE `users`.`id` = 1
  user Destroy (1.1ms)  DELETE FROM `users` WHERE `users`.`id` = 2
  TRANSACTION (4.4ms)  ROLLBACK
ActiveRecord::RecordNotDestroyed: Failed to destroy the record

Also, if the deletion is successful, an Array containing the target object that was successfully deleted is returned.

Correspondence

This Proposal made a PR before, but it was closed without being activated.

But I’m still looking for this feature so if community like this proposal, I’d like to work on this feature. What do you think?

5 Likes

I really like this idea, currently we have do something like:

ActiveRecord::Base.transaction do
  user.posts.each(&:destroy!)
  user.likes.each(&:destroy!)
end

destroy_all! would simplify this

1 Like