Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scaffolding ActiveRecord: two columns of the same data type

Another basic Rails question:

I have a database table that needs to contain references to exactly two different records of a specific data type.

Hypothetical example: I'm making a video game database. I have a table for "Companies." I want to have exactly one developer and exactly one publisher for each "Videogame" entry.

I know that if I want to have one company, I can just do something like:

script/generate Videogame company:references

But I need to have both companies. I'd rather not use a join table, as there can only be exactly two of the given data type, and I need them to be distinct.

It seems like the answer should be pretty obvious, but I can't find it anywhere on the Internet.

like image 239
Adam Rezich Avatar asked Jan 06 '09 16:01

Adam Rezich


3 Answers

Just to tidy things up a bit, in your migration you can now also do:

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

And since you're calling the keys developer_id and publisher_id, the model should probably be:

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

It's not a major problem, but I find that as the number of associations with extra arguments get added, the less clear things become, so it's best to stick to the defaults whenever possible.

like image 154
Jon Wood Avatar answered Oct 05 '22 17:10

Jon Wood


I have no idea how to do this with script/generate.

The underlying idea is easier to show without using script/generate anyway. You want two fields in your videogames table/model that hold the foreign keys to the companies table/model.

I'll show you what I think the code would look like, but I haven't tested it, so I could be wrong.

Your migration file has:

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

Then in your model:

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

You also mention wanting the two companies to be distinct, which you could handle in a validation in the model that checks that developer_id != publisher_id.

like image 25
mlibby Avatar answered Oct 05 '22 15:10

mlibby


If there are any methods or validation you want specific to a certain company type, you could sub class the company model. This employs a technique called single table inheritance. For more information check out this article: http://wiki.rubyonrails.org/rails/pages/singletableinheritance

You would then have:

#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

As a result, you would have Company, Developer and Publisher models to use.

 Company.find(:all)
 Developer.find(:all)
 Publisher.find(:all)
like image 43
thetacom Avatar answered Oct 05 '22 17:10

thetacom