Reputation: 6020
I have a rails model that has a non-defaulted boolean field that is nullable and am trying to set a default. I found a blog post about avoiding a 3-stat boolean issue, so am trying to cater for this. This is the migration I have:
def change
change_column :table, is_foo, :boolean, null: false, default: false
end
Running the migration fails because of existing null values in the database. What is the correct way to update existing entries to allow the schema change? Or should the not null control be added to the model:
validates :is_foo, presence: true
Not sure if adding this to the migration is the "right" way:
Table.update_all(:is_foo => false)
Similarly, this field was added by a migration without extra not null / default parameters. Would the migration to add column also require this, or would the default set the value? Here's the migration I ran:
add_column :table, is_foo, :boolean
If I had added ,null: false, default: false
on the add_column, would all the values have been set correctly?
Upvotes: 6
Views: 8676
Reputation: 2061
You could do it this way:
class UpdateFoo < ActiveRecord::Migration
def change
reversible do |direction|
direction.up {
Table.where(is_foo: nil).update_all(is_foo: false)
}
end
change_column :table, :is_foo, :boolean, null: false, default: false
end
end
When migrating up it will ensure first that all nulls are converted to false and then change the column to add your restrictions.
And yes, you could have avoided this if the first migration contained the restrictions.
I think you are also right adding the model validation.
Upvotes: 6
Reputation: 91
You can actually combine the change_column_null and change_column_default methods to accomplish this in a migration.
The change_column_null
method lets you add a NOT NULL constraint, with the last argument specifying what to replace any existing NULL values with.
The change_column_default
then sets the default for any new records.
class UpdateTable < ActiveRecord::Migration
def change
change_column_null :table, :is_foo, false, false
change_column_default :table, :is_foo, false
end
end
Upvotes: 9