Rails 5 Updating records in AR Relation

Mohit Natoo

By Mohit Natoo

on June 10, 2016

This blog is part of our  Rails 5 series.

The update_all method when called on an ActiveRecord::Relation object updates all the records without invoking any callbacks and validations on the records being updated.

Rails 5 supports update method on an ActiveRecord::Relation object that runs callbacks and validations on all the records in the relation.

1
2people = Person.where(country: 'US')
3people.update(language: 'English', currency: 'USD')
4

Internally, the above code runs update method on each Person record whose country is 'US'.

Let's see what happens when update is called on a relation in which validations fail on few records.

We have a Note model. For simplicity let's add a validation that note_text field cannot be blank for first three records.

1class Note < ApplicationRecord
2  validate :valid_note
3
4  def valid_note
5   errors.add(:note_text, "note_text is blank") if id <= 3 && note_text.blank?
6  end
7end

Now let's try and update all the records with blank note_text.

1
2 > Note.all.update(note_text: '')
3  Note Load (0.3ms)  SELECT `notes`.* FROM `notes`
4   (0.1ms)  BEGIN
5   (0.1ms)  ROLLBACK
6   (0.1ms)  BEGIN
7   (0.1ms)  ROLLBACK
8   (0.1ms)  BEGIN
9   (0.1ms)  ROLLBACK
10   (0.1ms)  BEGIN
11  SQL (2.9ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 3
12   (0.7ms)  COMMIT
13   (0.1ms)  BEGIN
14  SQL (0.3ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 4
15   (1.2ms)  COMMIT
16   (0.1ms)  BEGIN
17  SQL (0.3ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 5
18   (0.3ms)  COMMIT
19   (0.1ms)  BEGIN
20  SQL (3.4ms)  UPDATE `notes` SET `note_text` = '', `updated_at` = '2016-06-16 19:42:21' WHERE `notes`.`id` = 6
21   (0.2ms)  COMMIT
22
23 => [#<Note id: 1, user_id: 1, note_text: "", created_at: "2016-06-03 10:02:54", updated_at: "2016-06-16 19:42:21">,
24 #<Note id: 2, user_id: 1, note_text: "", created_at: "2016-06-03 10:03:54", updated_at: "2016-06-16 19:42:21">,
25 #<Note id: 3, user_id: 1, note_text: "", created_at: "2016-06-03 12:35:20", updated_at: "2016-06-03 12:35:20">,
26 #<Note id: 4, user_id: 1, note_text: "", created_at: "2016-06-03 14:15:15", updated_at: "2016-06-16 19:14:20">,
27 #<Note id: 5, user_id: 1, note_text: "", created_at: "2016-06-03 14:15:41", updated_at: "2016-06-16 19:42:21">,
28 #<Note id: 6, user_id: 1, note_text: "", created_at: "2016-06-03 14:16:20", updated_at: "2016-06-16 19:42:21">]
29

We can see that failure of validations on records in the relation does not stop us from updating the valid records.

Also the return value of update on AR Relation is an array of records in the relation. We can see that the attributes in these records hold the values that we wanted to have after the update.

For example in the above mentioned case, we can see that in the returned array, the records with ids 1, 2 and 3 have blank note_text values even though those records weren't updated.

Hence we may not be able to rely on the return value to know if the update is successful on any particular record.

For scenarios where running validations and callbacks is not important and/or where performance is a concern it is advisable to use update_all method instead of update method.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.