Rails 将 has_many 关系转换为 has 和 belongs to many

Rails Converting a has_many relationship into a has and belongs to many

我有一个 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 转换为具有许多通过的关系。我了解如何创建和连接连接 table,但我将如何获取现有数据并进行转换?

例如。在设施对象上有 region_id,因为设施可以属于多个区域,我可能需要一个 region_ids 字段并将区域集合铲到该列中,然后填充另一侧通过加入 table 加入协会。就向前推进和建立协会而言,我已经很清楚这部分了。但我不确定如何获取现有数据并将其转换过来,以便在我更改模型关联时应用程序不会中断。

如有任何建议,我们将不胜感激。

您需要添加另一个模型,一个名为 FacilityRegion.rb 的 "middle guy",如下所示:

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

我建议你始终使用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) 创建新的 table 并将新的关联添加到 类(我展示的)时创建新的迁移:

rails g migration migrate_database_state

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

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) 脚本成功 运行 后,创建新的迁移,从 facilities table 中删除 region_id 并删除这些关联定义(has_many :facilitiesbelongs_to :region) 来自模型。

一定是。我可能有一些错别字,请确保我没有遗漏任何内容

如果你想使用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

为了填充新连接 table,您需要在控制台中:

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

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