53

How do you write in the Rails way? I have a model - Managers. I want to delete all records from Managers that meet the condition that manager_level is 5.

Thank you.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
Noam B.
  • 3,120
  • 5
  • 25
  • 38

4 Answers4

89

I think it is better to use destroy instead of delete

because destroy will delete current object record from db and also its associated children record from db (https://stackoverflow.com/a/22757656/5452072)

Also delete will skip callbacks, but destroy doesn't.

Manager.where(:manager_level => 5).destroy_all
Toby 1 Kenobi
  • 4,717
  • 2
  • 29
  • 43
MurifoX
  • 14,991
  • 3
  • 36
  • 60
  • 7
    Definitely valid, but keep in mind: "Destroys the records matching conditions by instantiating each record and calling its destroy method." (http://apidock.com/rails/ActiveRecord/Relation/destroy_all) This could be very slow for large result sets. – Andrew Hare Oct 10 '12 at 18:42
  • 2
    Yeah, i was thinking about keeping models consistent and stuff. – MurifoX Oct 10 '12 at 20:11
  • 5
    @MurifoX - Can you explain why you think destroy is better than delete? Not necessarily disagreeing, but it would help make your answer more complete. – Phil DD Sep 13 '16 at 15:36
  • @PhilDD I had the same question and I think the answer is missing the reasoning on why to use destroy_all, not delete_all. I found the answer here: https://stackoverflow.com/a/22757533/5452072 – Sam Gh Nov 04 '18 at 19:39
  • Both destroy_all and delete_all have their place. When you want to delete 100k records quickly and you know that there are no callbacks or referential integrity issues, destroy_all takes forever for no benefit while delete_all does what you want quickly. When you want to cleanly delete 100 records and do all the Rails cleanup for them, destroy_all is what you want. – haslo Feb 26 '19 at 11:04
36

Try this:

Manager.delete_all(manager_level: 5)
Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • 1. I need to use a variable to specify what to delete. 2. The table does not have an index, it is a connector table between 2 other tables. – Noam B. Oct 11 '12 at 12:53
  • Didn't work for me. There was an error due to referential integrity. – Amin Ariana Oct 28 '14 at 18:09
  • 5
    this should be the accepted answer, as other queries try to SELECT before DELETING -- leading to performance problems on large datasets. – toobulkeh Oct 25 '17 at 17:40
  • 4
    passing conditions to `delete_all/destroy_all` has been [removed in rails 5.1](https://github.com/rails/rails/pull/27503/files#diff-eb992cfe9de67b368e9d3736ab1388d8L506). – Biwek Mar 02 '20 at 10:49
21

This should work:

Manager.where(:manager_level => 5).delete_all

Note: This will not remove dependent records.

iouri
  • 2,919
  • 1
  • 14
  • 11
9

After Rails 5.1., We cannot pass conditions to delete_all/destroy_all method

Manager.where(:manager_level => 5).delete_all

This will run multiple queries to delete each record

But in Rails 6, we can use delete_by to delete using condition,

Manager.delete_by(manager_level: 5)

This will run single delete query

delete from managers where manager_level = 5

Navarasu
  • 8,209
  • 2
  • 21
  • 32
  • I am not sure the Rails 6 model.delete_by(cond) is any faster than the model.where(cond).delete_all approach. You can see the implementation of delete_by on its original branch appears to just be syntactic sugar. https://github.com/abhaynikam/rails/commit/5c8d4c3466cbfa850a89f718ec358ed3a352c34e#diff-18a561656864ea240daf46bdb1f50faace49f9ef74b90bcf667d9bbb17fce084R532 – Barry Dec 17 '21 at 20:24