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_areas
到 root_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
。
在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_areas
到 root_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
。