Rails将has_many关系转换为has并属于many

时间:2016-01-26 16:50:34

标签: ruby-on-rails ruby-on-rails-3 activerecord associations rails-activerecord

我有一个Rails应用程序,其关系如下:

region.rb

class Region < ActiveRecord::Base
  has_many :facilities
end

facility.rb

class Facility < ActiveRecord::Base
  belongs_to :region
end

我想稍微扩展一下功能,以便设施一次可以属于多个区域。我相信我可以通过has_many_through关系来做到这一点,但是我需要一些指导才能将现有的has_many转换成有很多通道。我了解如何创建和连接连接表,但我如何获取现有数据并进行翻译呢?

所以举个例子。在设施对象上有region_id,因为设施可以属于多个区域,我可能需要region_ids字段并将区域集合铲入该列,然后填充该列通过连接表的关联的另一面。我有这个部分几乎已经找到了前进和连接协会。但我不确定如何获取现有数据并对其进行翻译,以便在我更改模型关联时应用程序不会中断。

非常感谢任何建议。

3 个答案:

答案 0 :(得分:4)

我建议您始终使用has_many :through代替HBTM。

要建立这种关系,您需要进行以下设置:

# region.rb
class Region
  has_many :facility_regions
  has_many :facilities, through: :facility_regions
end

# facility.rb
class Facility
  has_many :facility_regions
  has_many :regions, through: :facility_regions
end

# facility_region.rb
class FacilityRegion
  belongs_to :facility
  belongs_to :region
end

当然,您还需要创建迁移:

rails g migration create_facility_regions facility_id:integer region_id:integer
# in this migration create a uniq index:
add_index :facility_regions, %I(facility_id region_id), name: :facility_region
rake db:migrate

UPD

关于从一个数据库状态迁移到另一个数据库状态。

我认为这应该不是问题。

1)不要删除之前的关系(在模型中保留has_many :facilitiesbelongs_to :region)。

2)创建新表并将新关联添加到类(我展示的)时创建一个新的迁移:

rails g migration migrate_database_state

3)编写脚本,它将在db中创建新记录(以反映事物的当前状态):

ActiveRecord::Base.transaction do
  Facility.where.not(region_id: nil).find_each do |facility|
    next if FacilityRegion.find_by(falicity_id: facility.id, region_id: facility.region_id)
    FacilityRegion.create!(facility_id: facility.id, region_id: facility.region_id)
  end
end

4)将此脚本放入上次创建的迁移并运行它(或在没有迁移的控制台中,效果相同)。

5)成功运行脚本后,创建新的迁移,从region_id表中删除facilities并从模型中删除这些关联定义(has_many :facilitiesbelongs_to :region

一定是它。我可能会犯一些错别字,确保我没有错过任何内容和

答案 1 :(得分:1)

你需要添加另一个模特,一个&#34;中间人&#34;名为FacilityRegion.rb,如下所示:

facility.rb

class Facility < ActiveRecord::Base
  has_many :falicity_regions
  has_many :regions, through: falicity_regions
end

facility_region.rb

class FacilityRegion < ActiveRecord::Base
  belongs_to :region
  belongs_to :facility
end

region.rb

class Region < ActiveRecord::Base
  has_many :falicity_regions
  has_many :facilities, through: falicity_regions
end

答案 2 :(得分:0)

如果您想使用belongs_and_has_many关系,则需要:

rails g migration CreateJoinTableRegionsFacilities regions facilities

然后

rake db:migrate

现在,您的关系应该是:

Region.rb:

class Region < ApplicationRecord
  has_and_belongs_to_many :facilities
end

Facility.rb

class Facility < ApplicationRecord
  has_and_belongs_to_many :regions
end

要填充新的联接表,您需要在控制台中:

Region.all.find_each do |r|
  Facility.where(region_id: r.id).find_each do |f|
    r.facilities <<  f
  end
end

现在,您可以分别在Facility和Region表中保留列region_idfacility_id的列,也可以创建一个迁移来删除它。