Reversible migration for change_column_default from not having any default in Rails

13,914

Solution 1

Using 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

Solution 2

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

Solution 3

If you have an issue with migration file, Change your migration to this format.

change_column_default :table_name, :column_name, from: nil, to: "something"

Solution 4

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.

Share:
13,914

Related videos on Youtube

oligan
Author by

oligan

In part of my spare time, I work on fun programming projects. One was trying to analyze what underlies Wikipedia's Get to Philosophy game. I also worked on one called the "Small Eigen Collider". I'm currently learning Japanese, and I'm an active participant in lang-8.com, a website where you write journal entries in a language you're learning, and get corrected by native speakers of that language. In return, you correct people writing entries in your native language. Recently, I've been asking a few questions prompted by slightly incorrect English I've encountered on lang-8.

Updated on June 12, 2022

Comments

  • oligan
    oligan almost 2 years

    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 change method in Rails that's similar to the following:

    change_column_default :people, :height, from: nil, to: 0
    

    with the intention of going from not having any defaults, to having a default of zero.

    However, when I try rolling it back, I get

    ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration
    

    Considering I give Rails a from and to, why isn't it accepting it?

    I'm using Rails 4.2.0.

    • max
      max almost 9 years
      Could it be that Rails is trying to avoid a type coercion issue?
    • max
      max almost 9 years
    • Jonathan Allard
      Jonathan Allard almost 9 years
      I don't see any from or to parameters in the method definition
    • oligan
      oligan almost 9 years
      @JonathanAllard assuming that from and to are hash keys, rather than optional arguments, they wouldn't appear in the method definition.
    • oligan
      oligan almost 9 years
      @JonathanAllard how do you know that the method you link to is the one called by the change_column_default call within ActiveRecord::Migration?
    • Harish Shetty
      Harish Shetty almost 9 years
      Is it possible that the column is not nullable?
    • oligan
      oligan almost 9 years
      Speculation: the edge guides and Rails 4 guides may be saying different things, and that's because things have changed between Rails 4 and Rails edge.
    • Joshua Pinter
      Joshua Pinter over 5 years
      Using from and to was added in Rails 5+ in this commit: github.com/rails/rails/pull/20018/files
  • oligan
    oligan almost 9 years
    0 and nil are not data types. Also, you've mentioned the general concept of an irreversible migration, but you don't explain why this change would be an irreversible migration.
  • oligan
    oligan almost 9 years
    Where would I have set nil as the default value?
  • oligan
    oligan almost 9 years
    The method I called was change_column_default, not change_column.
  • oligan
    oligan almost 9 years
    from: nil, to: 0 is the same as {:from => nil, :to => 0}, which is a single object.
  • Deepesh
    Deepesh almost 9 years
    Actually I think you didn't get my point, the change_column_default accepts only a single value for setting the default value of column, now instead of that you are writing from: nil so it has no code written in the source which will process your from: nil and to: 0.
  • oligan
    oligan almost 9 years
    This answer isn't perfect, but it's better than the other answers (obviously, I can't award the bounty to myself).
  • Athar
    Athar almost 9 years
    yes @AndrewGrimm you are right regarding perfect answer. i added edgeguides.rubyonrails.org as reference which is yet to be realesed. Still for adding information, i checked this link api.rubyonrails.org/classes/ActiveRecord/Migration/… to see the list of reversible migrations. and i see change_column is not available in the list. lastly thank you for the bounty and thanks for your answer as well. helpful..:)