Blank wood

image by 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.

RailsConf 2024

I'm co-chairing RailsConf 2024 in Detroit May 7–9. Come and join us 

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.

Brighton Ruby 2024

Still running UK’s friendliest, Ruby event on Friday 28th June. Ice cream + Ruby 


Last updated on March 13th, 2023 by @andycroll

An email newsletter, with one Ruby/Rails technique delivered with a ‘why?’ and a ‘how?’ every two weeks. It’s deliberately brief, focussed & opinionated.