views:

4065

answers:

7

What is the best way to set default value in ActiveRecord?

I see a post from Pratik that describes an ugly, complicated chunk of code: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

YUCK!

I have seen the following examples googling around:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

and

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

I've also seen people put it in their migration, but I'd rather see it defined in the model code.

What's the best way to set default value for fields in ActiveRecord model?

+8  A: 

The Phusion guys have some nice plugin for this.

Milan Novota
A: 

This is what constructors are for! Override the model's initialize method.

Use the after_initialize method.

John Topley
Normally you'd be correct but you should never override initialize in an ActiveRecord model as it might not always be called. You should use the `after_initialize` method instead.
Luke Redpath
A: 
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end
end
Mike Breen
Mmmhh... seems ingenious at first, but after thinking a bit, I see a few problems. First, all the default values aren't in a single point, but scattered through the class (imagine searching for them or changing them). Second and worst, yo cannot put, later on, a null value (or even a false one!).
paradoja
why would you need to set a null value as default? you get that out of the box with AR without doing anything at all. As for false when using a boolean column then you're right, this is not the best approach.
Mike Breen
I can't speak for others coding habits I haven't had an issue because I don't scatter my getters/setters around a class file. Also, any modern text editor should make it easy to navigate to a method (shift-cmd-t in textmate).
Mike Breen
@paradoja - I take that back, I now see where it breaks down with using null also. Not necessarily using null as the default but if you actually wanted to change the value to null at some point. Good catch @paradoja, thanks.
Mike Breen
;) You're welcome.
paradoja
+12  A: 

We put the default values in the database through migrations (by specifying the :default option on each column definition) and let Active Record use these values to set the default for each attribute.

IMHO, this approach is aligned with the principles of AR : convention over configuration, DRY, the table definition drives the model, not the other way around.

Note that the defaults are still in the application (Ruby) code, though not in the model but in the migration(s).

Laurent Farcy
The problem is now the default value doesn't come into being for the typical 'new' action, which creates an empty AR record (no db access) and uses it to populate the form data.
ScottJ
Another problem is when you want a default value for a foreign key. You can't hard-code an ID value into the foreign key field because on different DBs the ID might be different.
shmichael
A: 

Although doing that for setting default values is confusing and awkward in most cases, you can use :default_scope as well. Check out squil's comment here.

skalee
+3  A: 

Sup guys, I ended up doing the following:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Works like a charm!

Tony
A: 

after_initialize method is deprecated, use the callback instead.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

however, using :default in your migrations is still the cleanest way.

Greg