Скаффолдинг ActiveRecord: два столбца одного типа данных

Еще один основной вопрос о Rails:

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

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

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

script/generate Videogame company:references

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

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


person Adam Rezich    schedule 06.01.2009    source источник


Ответы (3)


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

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"

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

person Jon Wood    schedule 06.01.2009
comment
Я попробовал это, и теперь у меня есть developer_id, но я не могу получить доступ к game.developer. В моем классе видеоигр есть attr_accessible :developer. - person quantumpotato; 04.06.2013
comment
Вероятно, это уже было решено, но добавили ли вы has_many :games (или что-то в этом роде) в свою модель разработчика? - person MrWater; 27.09.2013
comment
Как сделать обратное отображение? - person Gaurav Agarwal; 19.06.2014

Я понятия не имею, как это сделать с помощью 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.

person mlibby    schedule 06.01.2009
comment
В качестве примечания, параметр external_key здесь не нужен, так как по умолчанию используется имя ассоциации + _id. Для получения дополнительной информации см. spacevatican.org /2008/5/6/ - person Max Wallace; 15.08.2015

Если есть какие-либо методы или проверки, которые вы хотите использовать для определенного типа компании, вы можете создать подкласс модели компании. При этом используется метод, называемый наследованием одной таблицы. Для получения дополнительной информации ознакомьтесь с этой статьей: 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)
person thetacom    schedule 07.01.2009