views:

2990

answers:

5

Hi all,

I want to create a default value for an attribute by defining it in ActiveRecord. By default everytime the record is created, I want to have a default value for attribute :status. I tried to do this:

class Task < ActiveRecord::Base
  def status=(status)
    status = 'P'
    write_attribute(:status, status)
  end
end

But upon creation I still retrieve this error from the database:

ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null

Therefore I presume the value was not applied to the attribute.

What would be the elegant way to do this in Rails?

Many thanks.

+6  A: 

You can do it without writing any code at all :) You just need to set the default value for the column in the database. You can do this in your migrations. For example:

create_table :projects do |t|
  t.string :status, :null => false, :default => 'P'
  ...
  t.timestamps
end

Hope that helps.

Daniel Kristensen
This solution requires a database dump to preserve information in it.
EmFi
+7  A: 

You can set a default option for the column in the migration

OR

You can use a callback, before_save

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status = 'P' unless self.status
  end
end
Jim
Thanks, this is what I was looking for.
jpartogi
It should be `self.status`
Ryan Bigg
Radar, you're right. I'll fix it.
Jim
Normally we'd write self.status ||= 'P'. Also, if the field is being validated, consider using the before_validation callback.
tokland
this won't help you if you're trying to set a default value for the view (i.e., when creating a new record). A better option is after_initialize.
insane.dreamer
+2  A: 

The solution depends on a few things.

Is the default value dependent on other information available at creation time? Can you wipe the database with minimal consequences?

If you answered the first question yes, then you want to use Jim's solution

If you answered the second question yes, then you want to use Daniel's solution

If you answered no to both questions, you're probably better off adding and running a new migration.

class AddDefaultMigration < ActiveRecord::Migration
  def self.up
     change_column :tasks, :status, :string, :default => default_value, :null => false
  end
end

:string can be replaced with any type that ActiveRecord::Migration recognizes.

CPU is cheap so the redefinition of Task in Jim's solution isn't going to cause many problems. Especially in a production environment. This migration is proper way of doing it as it is loaded it and called much less often.

EmFi
A: 

I found a better way to do it now:

def status=(value) 
  self[:status] = 'P' 
end

In Ruby a method call is allowed to have no parentheses, therefore I should name the local variable into something else, otherwise Ruby will recognize it as a method call.

jpartogi
Your question should be changed to fit this accepted answer. In your question you wanted to default an initial value for an attribute. In the answer you wrote, you are really processing an inputted value for that attribute.
Jim
A: 
class MyModel < ActiveRecord::Base
  def initialize(attribs={})
    super(attribs)
    self.name = 'default' unless self.name.present?
    self
  end
end
Joseph Rodriguez
overriding initialize can have unintended consequences; not recommended. better to use the after_initialize callback
insane.dreamer