Default values in your models
This may be obvious but as it took me a little while to figure it out, I thought I'd share it with you.
If you want you have default values, the easiest way is to set your defaults within the table. But often, that is too late. In your new action, you want the defaults to appear within the form (or whatever). So instead, you have to set the defaults in the constructor - but in such a way that won't interfere with Active Record's standard behaviour.
In particular, there are several ways of instantiating an object:
thingy = Thingy.new
This creates a brand new thingy, with blank values (for example, in a new action).
thingy = Thingy.new params[:thingy]
This creates a brand new thingy, with values taken from the supplied parameters (for example, in a create action).
thingy = Thingy.find params[:id]
This creates a brand new thingy, loaded with values taken from the database.
Internally, ActiveRecord::Base's constructor takes a hash as a parameter - with a default value of nil. So if you want to override it, you have to honour its original semantics, otherwise the rest of the framework will complain.
So, imagine the Thingy model has attributes "name" and "price" that you want to initialise:
def initialise(params = nil)
super
self.name = "default"
self.price = 1.50
end
This won't work - because the second and third instantiations will have their values overridden. Instead, what you need to do is this:
def initialise(params = nil)
super
self.name = "default" unless self.name
self.price = 1.50 unless self.price
end
You could use
self.name ||= "default"
but I had one weird situation where it didn't work (unexplained) and I also find it more readable.
12 comments:
Thanks!
Though I have to say it's the American spelling of initialise they use. So it's "initialize".
By any chance was your unexplained one a boolean that you were defaulting to false?
The initialise/initialize always gets me!!
And I can't remember what the issue was but if it was then ||= false wouldn't work would it?
Baz: quite. I've just chucked together a wee plugin to make it explicit that I'm setting default values for certain attributes. It's completely undocumented and doesn't have a stand-alone test suite, but you can find it here:
http://svn.rubaidh.com/plugins/trunk/default_values
Basically you stick the following in your model:
default_values :foo => 3, :bar => "apples", :baz => false
and it does the right thing
Cheers fella,
I will take a look and update this article accordingly.
Thanks, I've been looking for this functionality for a while now!
Just curious, is there anything wrong with:
def after_initialize
return unless new_record?
self.someval = 'default_val'
end
I've not checked but i don't think there is an after_initialize on an AR::Base (there is on the Rails configuration object). So that wouldn't work. You could put something into before_validation - but the key thing is to test if a value has been put into the field already (as you may have overridden the default value by passing params to the constructor)
after_initialize does exist on AR, at least in 2.0.
Hi,
def after_initialize
return unless new_record?
self.someval = 'default_val'
end
will work, but it's doing a lot more: it does its job everytime you instantiate a database row, whereas what you want is only needed when creating a new AR. If you do a lot of AR.find(:all) calls, this is a lot of overhead.
Hi Rahoul,
I've started using your default_values plugin, but I get the following error whenever I do Model.find:
./script/../config/../vendor/rails/activerecord/lib/active_record/base.rb:286: warning: undefining `initialize' may cause serious problem
Any ideas?
Cheers, Chris.
Chris - it's not my plugin - you will need to contact Mathie at rubhaid.com for that.
Cheers
B
Post a Comment