wojja
wojja

Reputation: 355

Using renamed columns in a migration

In Rails 6.1, I would like to rename a column and convert the underlying data in a single migration:

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

Even after adding reset_column_information (from the docs: "Resets all the cached information about columns, which will cause them to be reloaded on the next request."), this throws NoMethodError: undefined method `new_column_name'.

The record example still has old_cloumn_name. I was expecting that the column information is updated in the database and the model after calling rename_column together with reset_column_information.

I can see in the sources that rename_column performs an alter table SQL command. Checking the column names via SQL after rename_column reveals that the column is renamed correctly. So, I assume that only the model holds outdated information.

Probably there are several workarounds (use SQL instead of model, do the rename after converting the data, use two separate migrations, ...), but I would prefer my approach for comprehensibility.

Upvotes: 0

Views: 1085

Answers (2)

Eyeslandic
Eyeslandic

Reputation: 14910

You have to rename it inside change_table if you want it to work as you are using it now.

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

Example.reset_column_information
# ....

Upvotes: 1

wojja
wojja

Reputation: 355

I think I found an anwser to my own question. As suggested in the migrations guide, it is possible to use a local model in combination with 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

This approach worked for me.

Upvotes: 1

Related Questions