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.
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.