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, чтобы поддерживать высокую производительность?
Ответы
Ответ 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