The Rails guides to active record migrations says that you can do
change_column_default :products, :approved, from: true, to: false
I\'ve got a
As of Oct 2016, This feature (using to:
and from:
for change_column_default
to be reversible) is now available on the 5.x branch. Unfortunately it's not available 4.2.x or lower. :(
Verification: git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5
in the rails project.
if you are using mysql as adapter, then according to this link http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default, your change_column_default
migration runs like this
def change_column_default(table_name, column_name, default) #:nodoc:
column = column_for(table_name, column_name)
change_column table_name, column_name, column.sql_type, :default => default
end
so as you see it calls change_column
within itself when you call change_column_default
and according to this link
http://edgeguides.rubyonrails.org/active_record_migrations.html
change_column
migration is irreversible.
This shows why you get ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
So if you want to run migration using change_column_default
you have to add def up
and def down
.
I would suggest to use change_column as it is already been called within change_column_default.
def up
change_column :people, :height, :integer, default: 0
end
def down
change_column :people, :height, :integer, default: nil
end
Just to add a few points. If you are stuck with the Rails version not supporting reversible change_column_default, one option to get past the problem is:
def change
# If your dataset is small, just cache in memory, if large, consider file dump here:
cache = Table.all
# Full column def important for reversibility
remove_column :table, :column, :type, { config_hash }
# Re-add the column with new default:
add_column :table, :column, :type, { config_hash, default: 0 }
# Now update the data with cached records (there might be more efficient ways, of course):
cache.each do |rec|
Table.find(rec.id).update(column: rec.column)
end
end
from
and to
was added in Rails 5+The guide linked to in the question is for Edge Rails, not for a released version of Rails.
Reversible syntax for change_column_default
is the result of pull request 20018. The pull request also updated the Rails guides for Edge Rails.
From activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
:
- def change_column_default(table_name, column_name, default)
+ # Passing a hash containing +:from+ and +:to+ will make this change
+ # reversible in migration:
+ #
+ # change_column_default(:posts, :state, from: nil, to: "draft")
+ #
+ def change_column_default(table_name, column_name, default_or_changes)
The pull request was made June 27, 2015, which is more recent than any version of Rails released as of August 1, 2015.
The documentation for migration for Rails 4.2.3 reflects the fact that reversible syntax is not yet available:
change_column_default :products, :approved, false
change_column_default :table_name, :column_name, from: nil, to: "something"
First of all as Jonathan Allard said the from
and to
are not in the method source which means that the change_column_default
doesn't accepts it. It is simply like this:
def sum(a)
return a
end
Now if you try and pass two variables to it like sum(a, b)
or anything it will not accept that right. This what you are trying to do above using from
and to
.
Now the correct syntax of this is:
change_column_default(:people, :height, 0)
The method doesn't accepts from
and to
(as it is defined as such, even if they are hash keys, if the method doesn't uses that key value pair anywhere then it is of no use to it) and if it is a new column the obviously it will have default value nil
(if not set before) and suppose if the column height
if of type integer
and you give it default value a
it will store 0
as the default value (not 100% sure but have tried doing this from rails console). It doesn't matters to rails what the default value currently is, it just needs the new default value. So if the current default value be 0
and you set it to nil
rails will not complaint. It is your database and you wish what to do with it. Just if the database interrupts it if you are doing something wrong like assigning string
to boolean
then it will obviously throw error.
Now once this migration has ran then it will set the default value to 0
now rails doesn't know what the previous default value was. As it is gone and it has not store that anywhere. So that is why change_column_default
is a irreversible migration. And if you try to roll it back it gives you ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
in case of change
method. Means when you have used:
def change
change_column_default(:people, :height, 0)
end
So that is why for this kind of migrations we use the method up
and down
:
def up
change_column_default(:people, :height, 0)
end
def down
change_column_default(:people, :height, nil)
end
Hope this helps.