如何将参考列更改为多态?

dei*_*iga 6 migration activerecord ruby-on-rails ruby-on-rails-2

我的模型(酒吧)已经有一个参考列,我们称之为foo_id,现在我需要改变foo_id,以fooable_id使之多态性.

我想我有两个选择:

  • 创建fooable多态的新参考列并从中迁移ID foo_id (迁移这些的最佳方法是什么?我可以做吗?Bar.each { |b| b.fooable_id = b.foo_id }?
  • 重命名foo_idfooable_id并添加polymorphicfooable_id.如何将多态添加到现有列?

Chr*_*lle 15

1.更改名称foo_idfooable_id在像迁移重命名它:

rename_column :bars, :foo_id, :fooable_id
Run Code Online (Sandbox Code Playgroud)

2.通过在迁移中添加所需的foo_type列,为其添加多态性:

add_column :bars, :fooable_type, :string
Run Code Online (Sandbox Code Playgroud)

3.并在您的模型中:

class Bar < ActiveRecord::Base
  belongs_to :fooable, 
    polymorphic: true
end
Run Code Online (Sandbox Code Playgroud)

4.最后种子你已经关联的类型如:

Bar.update_all(fooable_type: 'Foo')
Run Code Online (Sandbox Code Playgroud)

阅读定义多态ActiveRecord模型关联!


Mar*_*n M 10

Rails 更新 >= 4.2

当前的 Rails 生成带有索引和外键的引用,这是一件好事。
这意味着@Christian Rolle 的答案不再有效,因为在重命名后foo_id它会在bars.fooable_id引用时留下一个外键,foo.id这对其他fooables无效。

幸运的是,迁移也在发展,因此确实存在可撤销的引用迁移。

您需要创建一个新的引用并删除旧的引用,而不是重命名引用 ID。
新的是需要将旧引用中的 id 迁移到新引用。
这可以通过一个

Bar.find_each { |bar| bar.update fooable_id: bar.foo_id }
Run Code Online (Sandbox Code Playgroud)

但是当已经有很多关系时,这可能会很慢。Bar.update_all在数据库级别执行此操作,速度要快得多。

当然,你应该能够回滚迁移,所以当使用 foreign_keys 时,完整的迁移是:

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
Run Code Online (Sandbox Code Playgroud)

请记住,在回滚期间,更改是从下到上处理的,因此 foo_id 在 之前创建update_all,一切正常。

  • 清除对 Foo 之外的其他类型的所有多态引用可能很有用,因为当您关闭多态时,其他类不应再存在。这应该在稍后添加其他类的迁移(之前恢复:-)中完成。是的,但是…… (3认同)