Rails: Destroying vs Deleting

Rails offers several ways to remove records. They differ in whether they instantiate records, fire callbacks (including dependent: associations) and how they manage relation state afterward.

destroy_all

This is the definition of destroy_all:

# ActiveRecord::Relation
def destroy_all
  records.each(&:destroy).tap { reset }
end
  • records evaluates SQL, caches result
  • .each(&:destroy) iterates the cached Array, returns it
  • .tap { reset } calls Relation#reset, clears records and loaded, returns the Array unchanged

each(&:destroy) vs destroy_all

Both methods snapshot upfront, but the other does not reset the cache.

relation = Post.where(archived: true)

relation.destroy_all
relation.loaded?  # => false  (reset clears cache)
relation.count    # forces a new query

relation.each(&:destroy)
relation.loaded?  # => true   (stale destroyed objects remain cached)
relation.count    # does not query again

Irrelevant when the scope is called fresh each time (cleanup jobs), matters if reusing the relation variable.

destroy vs delete

dependent: options are registered as before_destroy callbacks. They only fire through destroy as it actually instantiates the records, never through delete as it's a pure SQL deletion.

delete_all deletes the records in bulk and also does not instantiate records nor does it run callbacks.

Example:

class Post < ApplicationRecord
  has_many :comments, dependent: :destroy  # before_destroy callback
end

post.destroy     # deletes post AND all comments (callback fires)
post.delete      # comments orphaned
Post.delete_all  # bulk SQL deletion, all children of posts orphaned
Felix Eschey