DannyB
DannyB

Reputation: 14776

Efficient delete_all with dependents in Rails

I am trying to perform delete_all on an ActiveRecord relation that has a lot of rows, and one level dependents.

When I used the destroy_all, it was of course painfully slow, since it iterated over the entire relation.

When using delete_all, I was unable to find a way to specify that rails should also delete (not destroy) its dependents.

This is what I know and tried:

  1. I have a Member model and a Message model.
  2. In the member model I have
    has_many :messages, inverse_of: :member, dependent: :destroy
  3. I thought that changing the above to dependent: :delete_all would delete all the dependents using one query (delete messages where member ID in [array of members to be deleted]) - it did not, and instead failed with a foreign key error.
  4. I have already spent time in the appropriate Rails documentation pages (like this one) and in some related StackOverflow questions (like this one).

The solution I found so far is this:

# Define the Member collection we want to delete
members = Member.where comment: 'debug'

# Get all the IDs from it
ids = members.pluck :id

# Delete all the dependent Message objects first
Message.where(member_id: ids).delete_all

# Then delete the Members
members.delete_all

This is fast, and properly results in two delete queries.

My questions are:

Upvotes: 0

Views: 354

Answers (1)

kurokyjr
kurokyjr

Reputation: 31

I have also done this before where I have overridden my destroy method. At the time, I did my extensive research and did exactly what you have done. If you end up overriding destroy, make sure to wrap your method in a transaction. This will protect you by rolling back your deletes if you get errors in one of your dependents.

Btw, you can chain your where and pluck and save 1 call from the db.

member_ids = Member.where(comment: 'debug').pluck(:id)

Upvotes: 1

Related Questions