change_column method for rails migrations support casting with a custom SQL statement. This allows us to change a column type and keep the former content as the new type. This way, we can for example prepare an address number column to hold German address numbers, which can contain letters:
class ChangeAnIntegerColumnToString < ActiveRecord::Migration[6.1] def up change_column :users, :address_number, 'varchar USING CAST(rating AS varchar)' end def down change_column :users, :address_number, 'int USING CAST(rating AS int)' end end
This migration's rollback strategy will fail, if any existing entry can't be casted. This is likely to happen, if you migrate the previous example, insert an entry with the value
12aand later try to rollback.
BE CAREFUL with this method, you should really be sure that it's okay to use an irreversible migration! if you know that no one will ever rollback your migration, you can define the
down method as follows:
def down raise ActiveRecord::IrreversibleMigration end
ActiveRecord::IrreversibleMigrationerror states that a migration can't be rolled back due to data loss.
The safest (but also most expensive) way would be to handle the cast yourself:
class ChangeAnIntegerColumnToString < ActiveRecord::Migration[6.1] class User < ActiveRecord::Base; end def up change_column :users, :address_number, 'varchar USING CAST(rating AS varchar)' end def down rename_column :users, :address_number, :address_number_string add_column :users, :address_number, :integer User.reset_column_information User.find_each do |user| # your cast here, e. g.: sanitized_address_number = user.address_number_string.remove(/\D/) user.update!(address_number: sanitized_address_number) end remove_column :users, :address_number_string end end