Blank wood

Keith Misner

Assign a default value to an attribute in Active Record

If you ever needed to set a default value in an instance of an Active Record model, you probably used a callback.

Since Rails 5.0 there’s been a better way. I had missed it until recently! (Thanks Moses!)

Instead of…

…assigning a default value in a callback:

class Message
  before_validation :assign_delivered_at

  # ...

  private

  def assign_delivered_at
    delivered_at ||= Time.zone.now
  end
end

Use…

…the Attribute API from Active Record:

class Message
  attribute :delivered_at, default: -> { Time.zone.now }

  # ...
end

Why?

Callbacks can be confusing to understand even when there’s a good reason to use them. Generally, the less you use them the fewer surprises you’ll have at a later date.

The attribute syntax is terser, clearer and the way Rails recommends to execute this behaviour.

There’s a lot more going on in this API—attribute changes tracking, type casting, adding attributes not backed by the database—but in this case we’re only using the default-setting capability.

Why not?

Ideally, from a data integrity perspective, it’d be better to set these defaults in the database schema.

Setting a default at the database level means Active Record will pull that value into a new, unsaved, model, so you’d be unlikely to need this approach.

Additionally, beware that setting a default in the Active Record model, as shown earlier, will overwrite any default set in the database when you call Model.new. Unless you’re deliberately looking to change the default when using Rails you don’t need to specify one using the Attribute API as well.

Sign up to get a nugget of Ruby knowledge every couple of weeks or so.

Last updated on March 13th, 2023