Rails:将has_many转换为has_one关联

时间:2014-02-25 20:40:41

标签: ruby-on-rails activerecord

假设我有一个包含以下型号的现有应用程序:

class User < ActiveRecord::Base
  has_many :locations, :through => :location_users
end

class Location < ActiveRecord::Base
  has_many :users, :through => :location_users
end

如何将此has_many转换为如下所示的has_one关联,就迁移,修剪多个位置的人的记录以及我错过的任何其他内容而言?有没有可以进行此转换的快捷方式?

class User < ActiveRecord::Base
  belongs_to :location
end

class Location < ActiveRecord::Base
  has_many :users
end

编辑:用户属于一个且只有一个位置

2 个答案:

答案 0 :(得分:1)

没有捷径。

编写迁移以将location_id添加到users

class AddLocationIdToUsers < ActiveRecord::Migration
  def change
    add_column :users, :location_id, :integer
  end
end

您可以编写另一个迁移来填充现有用户的location_id。 例如,如果要在locations_users表中填充用户的第一个location_id

class PopulateLocationIdOnUser < ActiveRecord::Migration
  def up
    #executing direct query to speed up the operation
    execute("update users set location_id = (select location_id from locations_users where locations_users.user_id = users.id limit 1)")
  end

  def down
    execute("update users set location_id = null")
  end
end

另一次迁移到drop_users表

class DropLocationsUsersTable < ActiveRecord::Migration
  def up
    drop_table :locations_users
  end

  def down
    create_table :locations_users do |t|
     #columns
    end
  end
end

您也可以通过一次迁移来完成所有这三个步骤。

答案 1 :(得分:1)

这并不是一个简单的方法。根据我的经验,你将不得不做很多手动工作。这就是我的方式:

  1. 编写迁移以将user_id添加到位置表。

  2. 运行迁移

  3. 添加has_one关系代码。 (就像你上面一样)

    class User < ActiveRecord::Base
      has_one :location
    end
    
    class Location < ActiveRecord::Base
     belongs_to :user
    end
    
  4. 编写迁移以转换所有现有数据。 (例如location.user = location.users.first)。但是在这种情况下写一个rake任务可能会更好,因为这只会发生一次,它需要在你的has_many关系代码中继存在。因此,删除has_many代码后,您的迁移将无效。

  5. 运行您的佣金任务

  6. 删除has_many代码和连接表。

  7. 完成所有这些之后,它应该都能正常工作。其他人可能有更好的方法,但这就是我做的方式。

相关问题