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

时间:2014-08-29 07:21:00

标签: ruby-on-rails activerecord migration ruby-on-rails-2

我的模型(Bar)已经有一个参考列,我们称之为foo_id,现在我需要将foo_id更改为fooable_id并使其变为多态。

我认为我有两个选择:

  • 创建具有多态性的新参考列fooable并从foo_id迁移ID(迁移这些ID的最佳方法是什么?我可以Bar.each { |b| b.fooable_id = b.foo_id }? < / LI>
  • foo_id重命名为fooable_id,并将polymorphic添加到fooable_id。如何在现有列中添加多态?

4 个答案:

答案 0 :(得分:13)

1。 foo_id 的名称更改为 fooable_id ,方法是在以下迁移中重命名:

rename_column :bars, :foo_id, :fooable_id

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

add_column :bars, :fooable_type, :string

3。,在您的模型中:

class Bar < ActiveRecord::Base
  belongs_to :fooable, 
    polymorphic: true
end

4. 最后播种已经关联的类型,如:

Bar.update_all(fooable_type: 'Foo')

阅读Define polymorphic ActiveRecord model association!

答案 1 :(得分:2)

我要做的一个小改动就是迁移:

#db/migrate/latest.rb
class Latest
  def change
    rename_column :bars, :foo_id, :fooable_id
    add_column :bars, :fooable_type, :string, after: :id, default: 'Foo'
  end
end

这将消除进行数据迁移的需要。

更新:这将适用于rails 3及更高版本。根据问题,原始基类隐含为Foo。

答案 2 :(得分:1)

Rails更新=> 4.2

Current Rails生成带有index和foreign_key的引用,这是一件好事。
这意味着@Christian Rolle的答案不再有效,因为重命名foo_id后,它在bars.fooable_id上引用了foo.id的外键对其他fooable无效。< / p>

幸运的是,迁移也在发展,因此确实存在无法撤消的参考迁移。

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

Bar.find_each { |bar| bar.update fooable_id: bar.foo_id }

但是当已经有很多关系时,这可能会非常慢。 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

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

答案 3 :(得分:0)

具体参考你的问题,这就是我要做的事情:

Bar模型中的所有数据都将存储在Bar模型的引用中。这意味着,如果您更改模型中的foo_id属性,则只需填充需要添加的bar_type属性(因为它们都可以引用相同的模型)

执行此操作的方法如下:

  
      
  1. foo_id&gt;创建迁移fooable_id
  2.   
  3. 插入fooable_type
  4.   
  5. 在rails控制台中,循环浏览Bar的所有现有记录,填写fooable_type
  6.   

首先要做的事情:

  

$ rails g migration ChangeFooID

#db/migrate/latest.rb
class Latest
   def change
      rename_column :bars, :foo_id, :fooable_id
      add_column :bars, :fooable_type, :string, after: :id
   end
end

这将为您创建各种列。然后你只需要能够循环记录和更改type列:

  

rails c

Bar.find_each do |bar|
  bar.update(barable_type: "Foo")
end

这将允许您更改列的类型,使您能够将所有当前记录与相应的记录相关联。


<强>多态性

您将能够use the Rails docs as a reference关于如何关联模型:

#app/models/foo.rb
class Foo < ActiveRecord::Base
   has_many :bars, as: :barable
end

#app/models/bar.rb
class Bar < ActiveRecord::Base
   belongs_to :foo, polymorphic: true
end

enter image description here