Ruby on rails 如何将引用列更改为多态?

Ruby on rails 如何将引用列更改为多态?,ruby-on-rails,activerecord,migration,ruby-on-rails-2,Ruby On Rails,Activerecord,Migration,Ruby On Rails 2,我的模型(条形图)已经有一个参考列,我们称它为foo\u id,现在我需要将foo\u id更改为fooable\u id,并使其具有多态性 我想我有两个选择: 创建新的引用列fooable,它是多态的,并从foo_ID迁移ID(迁移它们的最佳方式是什么?我可以只做Bar.each{b | b.fooable_ID=b.foo_ID}? 将foo\u id重命名为fooable\u id并将polymorphic添加到fooable\u id。如何将polymorpic添加到现有列 1.通过迁

我的模型(条形图)已经有一个参考列,我们称它为
foo\u id
,现在我需要将
foo\u id
更改为
fooable\u id
,并使其具有多态性

我想我有两个选择:

  • 创建新的引用列
    fooable
    ,它是多态的,并从
    foo_ID
    迁移ID(迁移它们的最佳方式是什么?我可以只做
    Bar.each{b | b.fooable_ID=b.foo_ID}?
  • foo\u id
    重命名为
    fooable\u id
    并将
    polymorphic
    添加到
    fooable\u id
    。如何将polymorpic添加到现有列

1.通过迁移将foo\u id的名称更改为fooable\u id,如下所示:

rename_column :bars, :foo_id, :fooable_id
2.并通过在迁移中添加所需的foo_type列向其添加多态性:

add_column :bars, :fooable_type, :string
#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
3.在您的车型中:

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

请阅读

关于您的问题,具体来说,以下是我要做的:

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

方法如下:

  • foo\u id
    fooable\u id
  • 插入一个
    fooable\u type
  • 在rails控制台中,循环浏览
    的所有现有记录,填充
    fooable_type
  • 第一件事:

    $rails g迁移ChangeFooID

    这将为您创建各种列。然后您只需要能够循环浏览记录并更改
    类型
    列:

    轨道c

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


    多态性

    您将能够了解如何关联模型:

    #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
    
    #app/models/foo.rb
    类Foo

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

    add_column :bars, :fooable_type, :string
    
    #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。

    rails更新>=4.2

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

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

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

    Bar.find_each { |bar| bar.update fooable_id: bar.foo_id }
    
    但是,当已经有很多关系时,这可能会非常慢。
    Bar.update\u all
    在数据库级别执行,速度要快得多

    当然,您应该能够回滚迁移,因此当使用外键时,完整的迁移是:

    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是在
    更新之前创建的,一切都很好。

    哦,哇,你还在使用Rails 2?这是真的还是错别字?bar.update(barable_type:“foo”)不起作用,因为更新是私有的。我猜你指的是ActiveRecord#update_attribute.um,这在Rails 2.3中无效,而且这也消除了使用非类型化基类的可能性。最后一步可以重写为
    Bar.update_all(fooable_type:'Foo')
    ,对吗?如果使用外键,这不起作用(默认用于生成的迁移以供参考)。请参阅下面的更新答案。谢谢!这真的很有帮助。关于下一部分的问题-它不应该显式地过滤
    type='Foo'
    fooable
    s,因为Foo和另一个类之间可能存在重复ID,该类可能在更改为多态后实现了fooable,对吗?清除所有polym可能很有用orphic引用到除Foo以外的其他类型,因为当您关闭多态性时,其他类不应再存在。这应该在以后添加其他类的迁移(还原为:-)中完成。所以有点像是的,但是。。。
    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