【问题标题】:Irreversible migration, can it be fixed? - Rails 4不可逆的迁移,可以修复吗? - 导轨 4
【发布时间】:2026-02-16 22:00:01
【问题描述】:

所以我做了这样的迁移

class AddDatetimeAttrToUsers < ActiveRecord::Migration
  def change
    change_column :users, :oauth_expires_at, :datetime
  end
end

在我的本地环境中它工作得很好,但是当我尝试时

heroku run rake db:migrate 我得到一个错误

ERROR:  column "oauth_expires_at" cannot be cast automatically to type timestamp without time zone
HINT:  Specify a USING expression to perform the conversion.

当我搜索它时,我创建了一个像这样的新迁移,作为使用 change 更改属性的最佳实践。

class PutDatetimeFieldToUsersExpireAtColumn < ActiveRecord::Migration
  def change
    remove_column :users, :oauth_expires_at
    add_column :users, :oauth_expires_at, :datetime
  end
end

所以我尝试使用 rake db:rollback 删除最后一次迁移并添加这个通知我最后一次迁移是不可逆的。

我的问题是,有没有办法真正回滚不可逆转的迁移,还是我应该只使用上面的新迁移进行迁移?

【问题讨论】:

  • 不可逆迁移是不可逆的,因为您破坏了数据。您可能对this answer 感兴趣,它可以让您知道如何在迁移中使用USING(如提示所建议的那样)。 (编辑:将链接更改为更好的链接)
  • 如何添加使用子句?

标签: ruby-on-rails ruby heroku


【解决方案1】:

在您的示例中,您有以下迁移文件:

class AddDatetimeAttrToUsers < ActiveRecord::Migration
  def change
    change_column :users, :oauth_expires_at, :datetime
  end
end

您已经在您的开发环境中成功运行rake db:migrate。您在本地运行 sqlite3 并在 heroku 上运行 PG,因此,您的 change_column 在本地运行,但它在 PG 上失败,因为 PG 需要“使用”语句。

解决此问题,第 1 步是编辑您的迁移文件,以按照上面 Yohann 的建议添加向上和向下迁移。是的,即使您已经完成了这次迁移,也应该这样做。

class AddDatetimeAttrToUsers < ActiveRecord::Migration
      def up
        change_column :users, :oauth_expires_at, :datetime
      end
      def down
        change_column :users, :oauth_expires_at, :time (or whatever type existed before datetime)
      end
    end

现在您可以运行rake db:rollback 并避免不可逆转的迁移错误,但前提是您没有添加额外的迁移。如果您添加了额外的迁移,您将需要使用rake db:down VERSION=2018xxxxxxxrake db:rollback STEP=X 指定要返回多远。

现在编辑迁移,使其与 pg 和 sqlite3 配合得很好:

class AddDatetimeAttrToUsers < ActiveRecord::Migration
          def up
            change_column :users, :oauth_expires_at, :datetime, using: 'oauth_expires_at::datetime'
          end
          def down
            change_column :users, :oauth_expires_at, :time

        end

现在您应该能够 rake db:migrate,推送到 heroku,然后 heroku 运行 rake db:migrate 并继续前进。

最后,您应该让 pg 在本地工作以匹配您的生产环境。

【讨论】:

    【解决方案2】:

    您可以在迁移中定义 updown 方法而不是 change。 这是一个例子:

    def up
      connection.execute %(create or replace view name_of_the_db_view)
    end
    
    def down
      connection.execute %(drop view name_of_the_db_view)
    end
    

    有了它,您将能够migraterollback 像正常迁移一样进行以前不可逆转的迁移。

    【讨论】:

      【解决方案3】:

      您似乎需要指定当前类型的 oauth_expires_at 列,因为在回滚时 Rails 应该知道它来创建列。我的意思是:

      remove_column :users, :oauth_expires_at, :string
      

      【讨论】:

        【解决方案4】:

        如果您敢于丢失本地数据库中的数据,那么您可以通过执行以下步骤从不可逆迁移(通过丢失数据库数据)中恢复:

        1-首先删除你的数据库(假设你在开发环境中,在这个环境中删除数据库就可以了——你的测试数据库也会消失)

        export RAILS_ENV=development
        
        
        rake db:drop
        

        2- 重新加载架构文件:

        rake db:schema:load
        

        3- 查看您当前的迁移文件:

        rake db:migrate:status
        

        4- 删除您想要摆脱的迁移:

        rake db:migrate:down VERSION=xxxxxx
        
        
        rails destroy migration migration_name
        

        5- 然后你可以让 db:migrate 迁移你的迁移。

        rake db:migrate
        

        【讨论】:

          最近更新 更多