Rails迁移:add_reference到表但外键比Rails公约的列名不同

Nei*_*eil 52 ruby-on-rails rails-migrations

我有以下两种型号:

class Store < ActiveRecord::Base
    belongs_to :person
end

class Person < ActiveRecord::Base
    has_one :store
end
Run Code Online (Sandbox Code Playgroud)

这是问题所在:我正在尝试创建迁移以在people表中创建外键.但是,引用Store外键的列未命名为store_id,因为它是rails约定,而是命名为foo_bar_store_id.

如果我遵循rails约定,我会像这样进行迁移:

class AddReferencesToPeople < ActiveRecord::Migration
  def change
    add_reference :people, :store, index: true
  end
end
Run Code Online (Sandbox Code Playgroud)

但是这不起作用,因为列名不是store_id,而是foo_bar_store_id.那么我如何指定外键名称只是不同,但仍保持索引:true以保持快速性能?

sch*_*pet 69

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

class AddFooBarStoreToPeople < ActiveRecord::Migration[5.0]
  def change
    add_reference :people, :foo_bar_store, foreign_key: { to_table: :stores }
  end
end
Run Code Online (Sandbox Code Playgroud)

  • 这也适用于 `create_table` 方向,如:`t.references :feature, foreign_key: {to_table: :product_features}` (6认同)
  • 这是看似无证的黑客之一,同时让我既爱又恨. (4认同)

Sia*_*Sia 66

在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
Run Code Online (Sandbox Code Playgroud)

是一篇关于这个主题的有趣博客文章.是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
Run Code Online (Sandbox Code Playgroud)

请注意,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
Run Code Online (Sandbox Code Playgroud)

有关belongs_tohas_one文档中的关联选项的更多信息,请参阅.


Mat*_*att 6

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

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

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

原始答案:

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

add_column :people, :foo_bar_store_id, :integer
add_index :people, :foo_bar_store_id
Run Code Online (Sandbox Code Playgroud)

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

class Person < ActiveRecord::Base
  has_one :store, foreign_key: 'foo_bar_store_id'
end
Run Code Online (Sandbox Code Playgroud)

  • 这不是数据库中的外键。 (47认同)
  • @matt我想他要问的是,这个问题专门询问有关在表(数据库级别)中创建外键而不只是在模型级别上创建外键的问题。我认为在更新的Rails 4.2中,使用引用生成的迁移可以在适当的表中添加外键,以帮助确保数据完整性。格式化存在问题,但我认为相关代码应类似于`add_foreign_key:people,:stores,column::foo_bar_store_id` (5认同)
  • @Matt阐明了Randy / baash05的意思,这不会在数据库中创建外键约束。也就是说,可以将foo_bar_store_id设置为无效的商店ID,该模型将通过所有验证。如果我错了,请纠正我,但是只要在模型中添加该行,滑轨就不会在保存时抛出ActiveRecord :: InvalidForeignKey:PG :: ForeignKeyViolation:ERROR错误。 (4认同)

Ric*_*eck 5

# 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
Run Code Online (Sandbox Code Playgroud)


too*_*keh 5

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

create_table :chapter do |t|
  t.references :novel, foreign_key: {to_table: :books}
  t.timestamps
end
Run Code Online (Sandbox Code Playgroud)

  • `t.references :novel, foreign_key: {to_table: :books}` 在 [Rails API 文档](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference) 中有说明) 但是它没有说明或提及可用于外键的其他选项。这些选项可以在 [add_foreign_key](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_foreign_key) 方法的文档中找到 (4认同)