Леса ActiveRecord: два столбца одного и того же типа данных

Другой основной вопрос Rails:

У меня есть таблица базы данных, которая должна содержать ссылки на две разные записи определенного типа данных.

Гипотетический пример: я создаю базу видеоигр. У меня есть таблица для "компаний". Я хочу иметь ровно один разработчик и ровно один издатель для каждой записи "Videogame".

Я знаю, что если я хочу иметь одну компанию, я могу просто сделать что-то вроде:

script/generate Videogame company:references

Но мне нужно иметь обе компании. Я бы предпочел не использовать таблицу соединений, так как может быть только ровно два из данного типа данных, и мне нужно, чтобы они были разными.

Кажется, что ответ должен быть довольно очевидным, но я не могу найти его нигде в Интернете.

Ответ 1

Просто для того, чтобы немного убирать вещи, в процессе миграции вы также можете:

create_table :videogames do |t|
  t.belongs_to :developer
  t.belongs_to :publisher
end

И поскольку вы вызываете ключи developer_id и publisher_id, модель должна быть:

belongs_to :developer, :class_name => "Company"
belongs_to :publisher, :class_name => "Company"

Это не серьезная проблема, но я нахожу, что по мере добавления количества ассоциаций с дополнительными аргументами становятся менее понятными вещи, поэтому лучше всего придерживаться значений по умолчанию, когда это возможно.

Ответ 2

Я не знаю, как это сделать с помощью script/generate.

Основная идея легче показать без использования script/generate в любом случае. Вы хотите, чтобы в вашей таблице/модели видеоигр два поля содержали внешние ключи для таблицы/модели компаний.

Я покажу вам, что я думаю код будет выглядеть, но я его не тестировал, поэтому я мог ошибаться.

В файле миграции есть:

create_table :videogames do |t|
  # all your other fields
  t.int :developer_id
  t.int :publisher_id
end

Затем в вашей модели:

belongs_to :developer, class_name: "Company", foreign_key: "developer_id"
belongs_to :publisher, class_name: "Company", foreign_key: "publisher_id"

Вы также указываете на то, что две компании должны быть различны, что вы могли бы обработать при проверке в модели, которая проверяет, что developer_id != publisher_id.

Ответ 3

Если существуют какие-либо методы или проверки, которые вы хотите использовать для определенного типа компании, вы можете подклассифицировать модель компании. Это использует метод, называемый наложением на одну таблицу. Для получения дополнительной информации ознакомьтесь с этой статьей: http://wiki.rubyonrails.org/rails/pages/singletableinheritance

Тогда у вас будет:

#db/migrate/###_create_companies
class CreateCompanies < ActiveRecord::Migration
  def self.up
    create_table :companies do |t|
      t.string :type  # required so rails know what type of company a record is
      t.timestamps
    end
  end

  def self.down
    drop_table :companies
  end
end

#db/migrate/###_create_videogames
class CreateVideogames < ActiveRecord::Migration
  create_table :videogames do |t|
    t.belongs_to :developer
    t.belongs_to :publisher
  end    

  def self.down
    drop_table :videogames
  end
end

#app/models/company.rb
class Company < ActiveRecord::Base 
  has_many :videogames
  common validations and methods
end

#app/models/developer.rb
class Developer < Company
  developer specific code
end

#app/models/publisher.rb
class Publisher < Company
  publisher specific code
end

#app/models/videogame.rb
class Videogame < ActiveRecord::Base 
  belongs_to :developer, :publisher
end

В результате вы могли бы использовать модели Company, Developer и Publisher.

 Company.find(:all)
 Developer.find(:all)
 Publisher.find(:all)