Подтвердить что ты не робот

Rails Migration: add_reference для таблицы, но различное имя столбца для внешнего ключа, а не для рельсов

У меня следующие две модели:

class Store < ActiveRecord::Base
    belongs_to :person
end

class Person < ActiveRecord::Base
    has_one :store
end

Вот проблема: я пытаюсь создать миграцию для создания внешнего ключа в таблице people. Однако столбец, ссылающийся на внешний ключ Store, не называется store_id, как было бы принято называть rails, но вместо этого называется foo_bar_store_id.

Если бы я шел по соглашению с рельсами, я бы сделал миграцию следующим образом:

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

Однако это не сработает, потому что имя столбца не store_id, а foo_bar_store_id. Итак, как я могу указать, что имя внешнего ключа просто отличается, но все еще поддерживает индекс: true, чтобы поддерживать высокую производительность?

4b9b3361

Ответ 1

В вашей миграции 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

РЕДАКТИРОВАТЬ: Несмотря на то, что это достигает цели иметь нетрадиционное имя столбца внешнего ключа, с индексированием, это не добавляет ограничение fk в базу данных. Смотрите другие ответы для более подходящих решений.

Ответ 2

В 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 Guides. Сообщение в блоге определенно помогло мне.

Что касается ассоциаций, явным образом указываю внешний ключ или имя класса, как это (я думаю, что ваши исходные ассоциации были переключены, так как "принадлежит_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 foo_bar_store_id

Подробнее о параметрах ассоциации в документации для belongs_to и has_one.

Ответ 3

в rails 5.x вы можете добавить внешний ключ в таблицу с другим именем, например так:

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

Ответ 4

# 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

Ответ 5

Чтобы расширить ответ schpet, это работает в директиве переноса на Rails 5 create_table следующим образом:

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

Ответ 6

Под обложками add_reference просто делегирует add_column и add_index, поэтому вам просто нужно позаботиться о себе:

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