在迁移中使用重命名的列

Using renamed columns in a migration

在Rails 6.1中,我想重命名一个列并在一次迁移中转换底层数据:

class RestructureExamples < ActiveRecord::Migration[6.1]
  
  def up
    rename_column :examples, :old_column_name, :new_column_name
    Example.reset_column_information
    Example.find_each do |example|
      unless example.new_column_name.nil?
        if example.new_column_name == 100
          example.new_column_name = 300
        else
          example.new_column_name = (example.new_column_name.to_f * 2.989).to_i
        end
      end
    end
  end

  def down
    # Do the reverse - left out for brevity
  end

end

即使在添加 reset_column_information 之后(来自文档:“重置所有关于列的缓存信息,这将导致它们在下一个请求时重新加载。”),这会抛出 NoMethodError: undefined method `new_column_name'

记录example还有old_cloumn_name。我期待在一起调用 rename_column 后在数据库和模型中更新列信息 with reset_column_information

我可以在 sources 中看到 rename_column 执行了一个 alter table SQL 命令。在 rename_column 之后通过 SQL 检查列名称表明该列已正确重命名。所以,我假设只有模型拥有过时的信息。

可能有几种解决方法(使用 SQL 代替模型,在转换数据后重命名,使用两个单独的迁移,...),但我更喜欢我的方法以便于理解。

如果您希望它像您现在使用的那样工作,您必须在 change_table 中重命名它。

change_table :examples do |t|
  t.rename :old_name, :new_name
end

Example.reset_column_information
# ....

我想我找到了自己问题的答案。正如 migrations guide 中所建议的,可以结合 reset_column_information:

使用本地模型
class RestructureExamples < ActiveRecord::Migration[6.1]
  
  class Example < ActiveRecord::Base
  end
  
  def up
    rename_column :examples, :old_column_name, :new_column_name
    Example.reset_column_information
    Example.find_each do |example|
      unless example.new_column_name.nil?
        if example.new_column_name == 100
          example.new_column_name = 300
        else
          example.new_column_name = (example.new_column_name.to_f * 2.989).to_i
        end
      end
    end
  end

  def down
    # Do the reverse - left out for brevity
  end

end

这种方法对我有用。