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.
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.
Last updated on March 13th, 2023