How to change a reference column to be polymorphic?
Update for Rails >= 4.2
Current Rails generates references with index and foreign_key, which is a good thing.
This means that the answer of @Christian Rolle is no longer valid as after renaming foo_id
it leaves a foreign_key on bars.fooable_id
referencing foo.id
which is invalid for other fooable
s.
Luckily, also the migrations evolve, so undoable migrations for references do exist.
Instead of renaming the reference id, you need to create a new reference and remove the old one.
What's new is the need to migrate the ids from the old reference to the new one.
This could be done by a
Bar.find_each { |bar| bar.update fooable_id: bar.foo_id }
but this can be very slow when there are already many relations. Bar.update_all
does it on database level, which is much faster.
Of course, you should be able to roll back the migration, so when using foreign_keys the complete migration is:
def change
add_reference :bars, :fooable, polymorphic: true
reversible do |dir|
dir.up { Bar.update_all("fooable_id = foo_id, fooable_type='Foo'") }
dir.down { Bar.update_all('foo_id = fooable_id') }
end
remove_reference :bars, :foo, index: true, foreign_key: true
end
Remember that during rollback, change is processed from bottom to top, so foo_id is created before the update_all
and everything is fine.
1. Change the name of foo_id to fooable_id by renaming it in a migration like:
rename_column :bars, :foo_id, :fooable_id
2. and add polymorphism to it by adding the required foo_type column in a migration:
add_column :bars, :fooable_type, :string
3. and in your model:
class Bar < ActiveRecord::Base
belongs_to :fooable,
polymorphic: true
end
4. Finally seed the type of you already associated type like:
Bar.update_all(fooable_type: 'Foo')
Read Define polymorphic ActiveRecord model association!