【问题标题】:How to migrate from using one foreign key to another in ActiveRecord?如何在 ActiveRecord 中从使用一个外键迁移到另一个外键?
【发布时间】:2015-10-29 22:44:35
【问题描述】:

使用 Postgres 作为后备存储,我有一个表(至少目前是这样),它有一个整数主键和一个带有唯一索引的 uuid。

在我的schema.rb 中看起来像这样(例如简化):

create_table "regions", force: cascade do |t|
  t.integer  "region_id" 
  t.uuid     "uuid",        default: "uuid_generate_v4()"
  t.string   "name"
end

add_index "regions", ["uuid"], name "index_regions_on_uuid", unique: true, using :btree

然后我有一个引用整数 id 的表,如下所示:

create_table "sites", force:cascade do 
  t.integer  "site_id"
  t.integer  "region_id"
  t.string   "name"
end

我要做的是从region_id 切换到uuid 作为第二个表中的外键。我应该怎么写这个迁移?

【问题讨论】:

  • 是否要将sites 表的region_id 的名称更改为uuid?或者您是否要更改 region_idregions 上为关联引用的列?
  • @eirikir 是后者,我想将sites.region_id 替换为引用regions.uuid 列的uuid 类型列,而不是regions.region_id 列。它可以是具有新名称的新列,也可以是相同的列,但可以进行更改,以实现更无缝的迁移和重构。
  • @derekv 我用关联代码更新了我的答案——现在在我看来它非常明确,我认为你没有理由不喜欢它:)
  • @EugZol 我们同时查看了您的答案和 eirikir 的答案并设法在本地工作,但是问题比最初看起来更棘手,因为您遇到了 catch22,其中迁移指的是关系在模型中定义但也改变了这种关系,所以当你的队友拉代码时它不会工作......也许这就是你暗指你编辑的答案,所以我会尽快给它一个镜头。
  • @derekv 你说得对,我专门解决了这个问题。

标签: ruby-on-rails ruby postgresql activerecord database-migration


【解决方案1】:

只需创建一个迁移,然后将一些 SQL 魔法吸入其中:

def up
  # Create and fill in region_uuid column,
  # joining records via still existing region_id column
  add_column :sites, :region_uuid

  if Site.reflect_on_association(:region).foreign_key == 'region_id'
    # We won't use 'joins(:regions)' in case we will need
    # to re-run migration later, when we already changed association
    # code as suggested below. Specifying join manually instead.
    Site.joins("INNER JOIN regions ON site.region_id = regions.id").update_all("region_uuid = regions.uuid")
  end

  drop_column :sites, :region_id
end

那么你只需要修复你的关联:

class Site < ActiveRecord::Base
  belongs_to :region, primary_key: :uuid, foreign_key: :region_uuid
end

class Region < ActiveRecord::Base
  has_many :sites, primary_key: :uuid, foreign_key: :region_uuid
end

【讨论】:

    【解决方案2】:

    从您的评论来看,您似乎想修改关联引用的主键,而不是外键。您实际上不需要迁移来执行此操作。相反,只需在每个模型中的关联定义上指定主键:

    Class Region << ActiveRecord::Base
      has_many :sites, primary_key: :uuid
    end
    
    Class Site << ActiveRecord::Base
      belongs_to :region, primary_key: :uuid
    end
    

    外键,因为它遵循被命名为belongs_to关系并附加"_id"(在本例中为region_id)的rails约定,因此不需要在此处指定。

    预计到达时间:您还需要确保sites.region_id 的类型与regions.uuid 的类型相匹配,我假设它是uuid。我还将假设该字段之前已编入索引(根据 ActiveRecord 约定)并且您仍希望它被编入索引。您可以像这样在迁移中更改所有这些:

    def up
      remove_index :sites, :region_id
      change_column :sites, :region_id, :uuid
      add_index :sites, :region_id
    end
    
    def down
      remove_index :sites, :region_id
      change_column :sites, :region_id, :integer
      add_index :sites, :region_id
    end
    

    【讨论】:

    • 数据库架构当前有sites.region_id 作为类型integer...所以我认为必须进行一些迁移,对吧?
    猜你喜欢
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    • 2020-06-19
    • 2016-11-03
    • 2020-06-27
    • 1970-01-01
    • 2016-08-18
    • 2017-10-24
    相关资源
    最近更新 更多