Rails 在非主键上添加外键

Rails add foreign key on non-primary key

在Rails5.1我有如下关系:

class RootArea < ApplicationRecord
  has_many :common_areas
end

class CommonArea < ApplicationRecord
  belongs_to :root_area, foreign_key: 'area_id', optional: true
end

以下是他们的迁移:

create_table "root_areas", force: :cascade do |t|
  t.integer "area_id"
  t.string "localname"
  t.string "globalname"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

create_table "common_areas", force: :cascade do |t|
  t.integer "area_id"
  t.string "localname"
  t.string "globalname"
  t.integer :root_area_id
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

我的目标是 "connect" common_areasroot_areas - on area_id 而不仅仅是 id- 这样单个根区域条目可以 "have" 多个公共区域。

例如美国(RootArea)有加利福尼亚(CommonArea)、得克萨斯(CommonArea)等

我尝试使用迁移和外键来执行此操作,如下所示:

add_foreign_key :common_areas, :root_areas, primary_key: :area_id

虽然起初这似乎运作良好,但最终还是失败了:

>>> RootArea.create!(area_id: 1111, 
                     localname: ’test', 
                     globalname: ’test') # OK

>>> CommonArea.create!(area_id: 9999, 
                       localname: ’test', 
                       globalname: ’test') # OK

>>> RootArea.first.common_areas << CommonArea.first # Error

=> # SQL (44.3ms)  UPDATE "common_areas" SET "root_area_id" = , "updated_at" =  WHERE "common_areas"."id" =   [["root_area_id", 19], ["updated_at", "2018-02-20 14:45:52.545450"], ["id", 1]]

输出表明 Rails 尝试将 CommonArea 的属性 root_area_id 设置为 RootArea 的主键(id 而不是 area_id).

例如,在上面的示例中,生成的 sql 语句应该将 root_area_id 设置为 1111 而不是 19.

今晚晚些时候,我从 Rails IRC 频道寻求进一步的帮助,最终用户 dionysus69 向我指出了这个 post,它与我正在寻找的非常相似.为了将来参考,这是最终解决方案:

class RootArea < ApplicationRecord
  has_many :common_areas, foreign_key: 'root_area_id', primary_key: 'area_id'
end

class CommonArea < ApplicationRecord
  belongs_to :root_area, foreign_key: 'root_area_id', primary_key: 'area_id', optional: true
end

现在我可以成功了

>>  RootArea.first.common_areas << CommonArea.first

并将 root_area_id 设置为正确的 area_id 值而不是 id