0

I have something like this:

class AddTestToPeople < ActiveRecord::Migration
  def change
    add_column :people, :status, :string, default: "normal"
  end
end

class Person < ActiveRecord::Base
  validates_inclusion_of :status, in: [ "normal", "super" ]
end

...and the default status value of "normal" doesn't pass the validation when a new record is created. I guess I could just drop the default value, but I'm curious why this doesn't work. Can someone enlighten me?

ivan
  • 6,032
  • 9
  • 42
  • 65
  • What do you mean by "`status` value of `normal` doesn't pass the validation when a new record is created"? Do you get an error? Share the results in the question so people can help you better. – Kirti Thorat Mar 17 '14 at 14:12
  • Problem with using defaults in the database is that they don't get set until the object is saved. If you want to construct a new object with the defaults set at once, you need to set them in the initializer callback. See http://stackoverflow.com/questions/328525/how-can-i-set-default-values-in-activerecord – Danny Mar 17 '14 at 14:20

1 Answers1

1

Default value is set in database.

When you try to insert a record in people table with status attribute set as nil, only then the default value normal would be inserted in the database against status column.

If you are not passing any value to status attribute while saving a new record, its value would be nil. Hence, the validation won't pass. Status would only be set to "normal" at the time of inserting the record.

I would suggest you to modify the model as below, database would take care of the default value:

class Person < ActiveRecord::Base
  validates_inclusion_of :status, in: [ "super" ], allow_nil: true
end

Or

Second option would be, as Danny suggested, set up an after_initialize callback and set the default value of status when its not specified. If you take up this option then I don't think that you need a default value at DB level as it status field would always be set from Model.

class Person < ActiveRecord::Base
  after_initialize :init_status, if: :new_record?
  validates_inclusion_of :status, in: [ "normal","super" ]

  def init_status
    self.status ||= "normal" 
  end
end
Kirti Thorat
  • 52,578
  • 9
  • 101
  • 108
  • For me this makes the default very "ambiguous": you explicitly declare that you allow nil, then overwrite it at database level. I'd prefer to use an initializer in this case, making it clear for every team member what the real default is... – Danny Mar 17 '14 at 14:23
  • @Danny I added it as a second option for OP. Although, if thats done, I don't see the need of having a `default value` in database as it will always be set from the Model. – Kirti Thorat Mar 17 '14 at 15:47
  • 1
    You're absolutely right. Setting it at database level doesn't "fit" with the validation model in Rails. I always put the defaults in the model, and use the database purely as "storage" medium. I'd also add a "if: :new_record?" to the after_initialize, as to make it more explicit an "after creation" default – Danny Mar 17 '14 at 15:51