Rails 7 allows setting timestamps on insert_all/upsert_all record creation


ActiveRecord is one of the most useful utilities in the Rails toolkit. It allows us to perform complex database queries while abstracting the adapter, essentially making a database “pluggable”.

However, some edge cases stand out like a sore thumb. One such query is the ability to set values to the standard Rails timestamps which are available across models. Namely, created_at, created_on, updated_at, updated_on.

In a typical model insertion, one does not need to actively set values for the timestamps. They are set by the underlying framework at the time of insertion.

> Book.create(title: "Ruby on Rails Tutorial: Learn Web Development with Rails")

> Book.last.created_at
=> Tue, 18 Jan 2022

Before

One would expect consistent action across similar queries. Let’s have a look at mass insertion queries.

> Book.insert_all[
  {title: "Ruby on Rails Tutorial: Learn Web Development with Rails"},
  {title: "Service-Oriented Design with Ruby and Rails"}
]

=> Traceback (most recent call last):
        1: from (irb):1
ActiveRecord::NotNullViolation (SQLite3::ConstraintException: NOT NULL constraint failed: books.created_at)

It raises an unexpected exception ActiveRecord::NotNullViolation, since a database constraint is present on the created_at column. This leaves us with only one option — manually providing values for all timestamp columns.

The only way to make this work is,

> Book.insert_all[
  {title: "Ruby on Rails Tutorial: Learn Web Development with Rails", created_at: Time.now, updated_at: Time.now},
  {title: "Service-Oriented Design with Ruby and Rails", created_at: Time.now, updated_at: Time.now}
]

=> #<ActiveRecord::Result:0x00007f9ffcdfb620 @columns=[], @rows=[], @hash_rows=nil, @column_types={}>

After

Fortunately, Rails 7 allow setting timestamps on insert_all / upsert_all record queries.

> Book.insert_all[
  {title: "Ruby on Rails Tutorial: Learn Web Development with Rails"},
  {title: "Service-Oriented Design with Ruby and Rails"}
]

=> #<ActiveRecord::Result:0x00007f9ffcdfb620 @columns=[], @rows=[], @hash_rows=nil, @column_types={}>

We have the option to override this configuration using,

> Book.insert_all[
  {title: "Ruby on Rails Tutorial: Learn Web Development with Rails"},
  {title: "Service-Oriented Design with Ruby and Rails"}
], record_timestamps: false

=> Traceback (most recent call last):
        1: from (irb):1
ActiveRecord::NotNullViolation (SQLite3::ConstraintException: NOT NULL constraint failed: books.created_at)

Which, as expected, will result in the previous error.

Join Our Newsletter