Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running validations when using `update_all`

Tags:

According to the Rails docs here and here, using update_all does not do the following -

  • It skips validations
  • It does not update the updated_at field
  • It silently ignores the :limit and :order methods

I'm trying to go through my code base and remove instances of update_all, particularly because of the first point.

Is there a way to still have the convenience of update_all and still run validations? I understand that I can loop through each record and save it, but that's not only messier visually but also more more inefficient because it executes N SQL statements instead of 1

# before User.where(status: "active").update_all(status: "inactive") # after User.where(status: "active").each { |u| u.update(status: "inactive") } 

Thanks!

Edit: I'm using Rails 4.2

like image 732
user2490003 Avatar asked Sep 22 '16 16:09

user2490003


People also ask

Does Update_all trigger callbacks?

Does Update_all trigger callbacks in rails? permalink #update_all(updates) ⇒ Object It does not instantiate the involved models and it does not trigger Active Record callbacks or validations.

What does Update_all return?

ActiveRecord's update_all() returns the number of records updated.

How does validate work in Rails?

Rails validation defines valid states for each of your Active Record model classes. They are used to ensure that only valid details are entered into your database. Rails make it easy to add validations to your model classes and allows you to create your own validation methods as well.

Does Update_all update Updated_at?

Timestamps. Note that ActiveRecord will not update the timestamp fields (updated_at/updated_on) when using update_all().


1 Answers

Unfortunately update_all is way faster because it doesn't instantiate an active record object for each record and instead deals directly with the database. In your case, since you need validations and callbacks, you'll need to instantiate the objects and so you're best bet is iterating in batches of 1000 and performing the update as originally shown. Such as:

User.where(status: "active").find_each { |u| u.update(status: "inactive") } 

The find_each method only loads 1000 objects at a time thus not overloading the garbage collector. If you have bulk records in the hundreds of thousands of rows I'd consider going back to update_all or moving the updating to a background task since it can easily cause a timeout when deployed.

like image 200
bkunzi01 Avatar answered Sep 20 '22 01:09

bkunzi01