The blog of Rahoul Baruah from 3hv Ltd

What's going on?

My name is Rahoul Baruah (aka Baz) and I'm a software developer in Leeds (England).

This is a log of things I've discovered while writing software in Ruby on Rails. In other words, geek stuff.

However, I've decided to put this blog on ice - I would ask you to check out my business blog here (or subscribe here).

18 January, 2007

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:

Michael said...

Thanks!

Though I have to say it's the American spelling of initialise they use. So it's "initialize".

Graeme Mathieson said...

By any chance was your unexplained one a boolean that you were defaulting to false?

Baz said...

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?

Graeme Mathieson said...

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

Baz said...

Cheers fella,

I will take a look and update this article accordingly.

Alex said...

Thanks, I've been looking for this functionality for a while now!

Anonymous said...

Just curious, is there anything wrong with:

def after_initialize
return unless new_record?
self.someval = 'default_val'
end

Baz said...

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)

Anonymous said...

after_initialize does exist on AR, at least in 2.0.

Dirk Groten said...

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.

chris said...

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.

Baz said...

Chris - it's not my plugin - you will need to contact Mathie at rubhaid.com for that.

Cheers
B

eXTReMe Tracker