Rails迁移:表的add_reference但外键的列名与Rails Convention不同


73

我有以下两个模型:

class Store < ActiveRecord::Base
    belongs_to :person
end

class Person < ActiveRecord::Base
    has_one :store
end

这是问题所在:我正在尝试创建一个迁移,以在人员表中创建外键。但是,引用Store外键的列并未像rails约定那样被命名为store_id,而是被命名为foo_bar_store_id

如果我遵循Rails约定,我将进行如下迁移:

class AddReferencesToPeople < ActiveRecord::Migration
  def change
    add_reference :people, :store, index: true
  end
end

但是,这将不起作用,因为列名不是store_id而是foo_bar_store_id。因此,如何指定外键名称只是不同,但仍保持index:true以保持快速性能?

Answers:


9

编辑:对于那些看到滴答声,不要继续阅读!

尽管此答案实现了使用索引建立具有非常规外键列名称的目标,但并未向数据库添加fk约束。请参阅其他答案,以使用以下更合适的解决方案add_foreign_key和/或'add_reference'。

注意:总是看看其他答案,公认的答案并不总是最好的!

原始答案:

AddReferencesToPeople迁移中,您可以使用以下方法手动添加字段和索引:

add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id

然后让您的模型知道外键,如下所示:

class Person < ActiveRecord::Base
  has_one :store, foreign_key: 'foo_bar_store_id'
end

53
这不是数据库中的外键。
baash05

@ baash05您能解释您的问题吗?
马特

6
@matt我认为他要问的是,这个问题专门询问有关在表(数据库级别)中创建外键而不只是在模型级别上创建外键的问题。我认为在4.2的新版本中,使用引用生成的迁移可以在适当的表中添加外键,以帮助确保数据完整性。格式化存在问题,但我认为相关代码应类似于add_foreign_key :people, :stores, column: :foo_bar_store_id
Randy Shelford

1
@RandyShelford谢谢,但是我也不认为这解释了baash05的评论。我的答案包括通过迁移在数据库中创建外键,以及在模型中处理非标准外键。我的答案遗漏了什么?您对的建议add_foreign_key已被此处的另一个答案覆盖。
马特

5
@Matt阐明了Randy / baash05的意思,这不会在数据库中创建外键约束。也就是说,foo_bar_store_id可以将其设置为无效的商店ID,并且模型将通过所有验证。如果我错了,请纠正我,但是只要在模型中添加该行,滑轨就不会在保存时抛出ActiveRecord :: InvalidForeignKey:PG :: ForeignKeyViolation:ERROR错误。
CHawk

110

在rails 5.x中,您可以将外键添加到具有不同名称的表中,如下所示:

class AddFooBarStoreToPeople < ActiveRecord::Migration[5.0]
  def change
    add_reference :people, :foo_bar_store, foreign_key: { to_table: :stores }
  end
end

1
@inye好像是错误的被固定👍
schpet

12
这是那些看似没有证件的骇客之一,这些骇客使我同时爱恨恨Rails。
Peelman

4
这也适用于以下create_table方向:t.references :feature, foreign_key: {to_table: :product_features}
toobulkeh

爱恨交加关系是最好的,但数量合适
ARK

当我在MySQL中执行此操作时,我得到ActiveRecord :: MismatchedForeignKey:incoming_location_idinventory_incomings上的列idaddresses类型上的上的列不匹配bigint(20)。要解决此问题,请将incoming_location_id列的类型更改inventory_incomings为:bigint。
hook38 '20

77

在Rails 4.2中,您还可以使用自定义外键名称设置模型或迁移。在您的示例中,迁移将是:

class AddReferencesToPeople < ActiveRecord::Migration
  def change
    add_column :people, :foo_bar_store_id, :integer, index: true
    add_foreign_key :people, :stores, column: :foo_bar_store_id
  end
end

是有关此主题的有趣博客文章。是《 Rails指南》中的半加密部分。博客文章绝对帮助了我。

对于关联,请像下面这样显式地声明外键或类名(我认为您的原始关联会随着带有外键的类中的“ belongs_to”进入而被切换):

class Store < ActiveRecord::Base
  has_one :person, foreign_key: :foo_bar_store_id
end

class Person < ActiveRecord::Base
  belongs_to :foo_bar_store, class_name: 'Store'
end

请注意,class_name项目必须是字符串。foreign_key项可以是字符串或符号。从本质上讲,这使您可以使用语义命名的关联来访问漂亮的ActiveRecord快捷方式,如下所示:

person = Person.first
person.foo_bar_store
# returns the instance of store equal to person's foo_bar_store_id

有关文档,请参阅有关属地文档has_onehas_one


1
add_column:people,:foo_bar_store_id,index:true应该声明一个类型吗?
Chase

1
@alex是-最好有单独的上下方法,而不是仅使用一种更改方法。向上看起来可能与答案的更改方法相同,向下看起来将删除该列。
新航

9

为了扩展schpet的答案,它可以在create_tableRails 5迁移指令中工作,如下所示:

create_table :chapter do |t|
  t.references :novel, foreign_key: {to_table: :books}
  t.timestamps
end

4
t.references :novel, foreign_key: {to_table: :books}Rails API文档中进行了说明,但未说明或提及可用于外键的其他选项。这样的选项可以在文档中找到add_foreign_key方法
Jignesh Gohel

@JigneshGohel感谢您链接到文档!我以前没遇到过
艾莉森

6
# Migration
change_table :people do |t|
  t.references :foo_bar_store, references: :store #-> foo_bar_store_id
end

# Model
# app/models/person.rb
class Person < ActiveRecord::Base
  has_one :foo_bar_store, class_name: "Store"
end

很高兴有人也添加了模型参考;节省了我一些时间!
Bigtrizzy

0

在幕后,add_reference只是委派给add_column和add_index,因此您只需要自己照顾一下即可:

add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id

Person模型如何知道foo_bar_store_id实际上是store_id的外键?
尼尔

4
那不属于迁移,而是属于模型中的关联定义。正如马特(Matt)提到的那样has_one :store, foreign_key: 'foo_bar_store_id'
博尔德(Boulder)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.