首页 > 解决方案 > 如何回滚此 Rails 迁移?

问题描述

我运行了以下迁移并忘记包含我想要的默认值。我正在尝试回滚它,以便我可以添加默认值并再次前滚。

20190728151635_add_cooldown_to_skill_levels.rb

class AddCooldownToSkillLevels < ActiveRecord::Migration[5.1]
  def change
    add_column :skill_levels, :cooldown, :integer
  end
end

所有 rails db:migrate 和 db:rollbacks 都失败并出现以下错误

$ rails db:回滚

== 20190728151635 AddCooldownToSkillLevels: reverting =========================
-- remove_column(:skill_levels, :cooldown, :integer)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "skill_levels"



And 100+ more lines ...

我尝试将迁移更改为具有明确的向上/向下方法无济于事

class AddCooldownToSkillLevels < ActiveRecord::Migration[5.1]
  def up
    add_column :skill_levels, :cooldown, :integer
  end

  def down
    remove_column :skill_levels, :cooldown
  end
end

我尝试添加一个只添加默认值的新迁移。

20190728153208_add_default_value_to_cooldown_on_skill_levels.rb

class AddDefaultValueToCooldownOnSkillLevels < ActiveRecord::Migration[5.1]
  def change
    change_column_default :skill_levels, :cooldown, 1
  end
end

类似的错误 $ rails db:migrate

== 20190728153208 AddDefaultValueToCooldownOnSkillLevels: migrating ===========
-- change_column_default(:skill_levels, :cooldown, 1)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "skill_levels"

And 100+ more lines ...

我很乐意直接在 SQL 中修复它,但我试图保持我的迁移完好无损。

编辑:添加 $ rails db:migrate:status 的输出

...
   up     20190727160901  Create skill levels
   up     20190728004535  Create skill effects
   up     20190728151635  Add cooldown to skill levels
  down    20190728153208  Add default value to cooldown on skill levels

感谢你的帮助。

标签: ruby-on-railssqliteruby-on-rails-5rails-migrations

解决方案


The error:

FOREIGN KEY constraint failed: DROP TABLE "skill_levels" 

is telling you that there is a foreign key constraint that references your skill_levels table (i.e. t.references :skill_levels, foreign_key: true in some other migration) and the remove_column :skill_levels, :cooldown is causing a problem with that.

But why would a database constraint be a problem during an ALTER TABLE to remove a column? Well, SQLite's ALTER TABLE doesn't support removing a column, instead you have to (see the SQLite FAQ or How to delete or add column in SQLITE?):

  1. Create a new table without the column that you're removing.
  2. Copy the data to the new table.
  3. Drop the original.
  4. Rename the copy.

ActiveRecord does this behind the scenes for you. Step (3) is where things are going wrong because you can't drop a table that is being referenced by foreign key constraints. I think you'll have handle the foreign key by hand: first use separate up and down methods, then modify the down method to:

  1. Drop all the foreign keys that reference your table, you can use remove_foreign_key for this once you know what FKs you're dealing with.
  2. Then remove_column :skill_levels, :cooldown as you have now.
  3. And finally put all the FKs back, you can use add_foreign_key for this.

Something like:

def down
  remove_foreign_key :some_table, :skill_levels
  remove_column :skill_levels, :cooldown
  add_foreign_key :some_table, :skill_levels
end

Ideally ActiveRecord would take care of this for you but presumably AR's SQLite support is mostly from before AR supported foreign key constraints.


推荐阅读