I think you have your belongs_to/has_many a little backwards.
belongs_to and has_many
For your example,
class Tp < ActiveRecord::Base
has_many :dogs
has_many :cats
has_many :stars
end
Tp doesn't have any dog_id, cat_id, or star_id in its database row. When you set belongs_to in the other models,
class Dog < ActiveRecord::Base
belongs_to :tp
end
class Cat < ActiveRecord::Base
belongs_to :tp
end
class Star < ActiveRecord::Base
belongs_to :tp
end
You should add a tp_id to each model (dogs table, cats table, and stars table).
Then, calling
tp = Tp.find(123)
Finds Tp with id equal to 123
SELECT * FROM tps WHERE id = 123;
And calling
cats = tp.cats
dogs = tp.dogs
stars = tp.stars
Finds all Cat, Dog, and Star instances with tp_id equal to 123
SELECT * FROM cats WHERE tp_id = 123;
SELECT * FROM dogs WHERE tp_id = 123;
SELECT * FROM stars WHERE tp_id = 123;
If you need your Cat instances to belong to many Tp instances, and Tp instances to have many Cat instances, then you should look at Rails's has_and_belongs_to_many or has_many :through.
has_and_belongs_to_many
A has_and_belongs_to relationship would require new tables cats_tps, dogs_tps, and stars_tps. These tables would have a schema of
cats_tps
cat_id
tp_id
dogs_tps
dog_id
tp_id
stars_tps
star_id
tp_id
Then in your models
class Tp < ActiveRecord::Base
has_and_belongs_to_many :dogs
has_and_belongs_to_many :cats
has_and_belongs_to_many :stars
end
class Dog < ActiveRecord::Base
has_and_belongs_to_many :tps
end
class Cat < ActiveRecord::Base
has_and_belongs_to_many :tps
end
class Star < ActiveRecord::Base
has_and_belongs_to_many :tps
end
Now, running
tp = Tp.find(123)
cats = tp.cats
Generates the SQL
SELECT "cats".* FROM "cats" INNER JOIN "cats_tps" ON "cats"."id" = "cats_tps"."cat_id" WHERE "cats_tps"."tp_id" = 123;
Which is essentialy the query (get me a list of all the cat_ids that belong to Tp 123) and then (get me all the cats that match these cat ids).
Generating the cats_tps join table can be done with a migration like
class CreateCatsTps < ActiveRecord::Migration
def change
create_table :cats_tps, :id => false do |t|
t.belongs_to :cat
t.belongs_to :tp
end
end
end
This works great for simple joins, but you may want to look into using has_many :through. This is because the cats_tps table holds no information about when or why this Cat belongs to a Tp or this Tp belongs to the Cat. Likewise, if you add Bird, Horse, Frog, and Snake models, you will have to create birds_tps, horses_tps, frogs_tps, and snakes_tps tables. Yuck.
has_many :through
To create a has_many :through relationship, you create a new model that makes sense semantically that links a Tp to a Cat. For instance, let's say that a Tp walks cats. You could create an Walk model that links a Cat to a Tp.
class Walk < ActiveRecord::Base
belongs_to :cat
belongs_to :tp
attr_accessible :price, :duration, :interval # these attributes describe the Walk relationship
end
class Cat < ActiveRecord::Base
has_many :walks
has_many :tps, :through => :walks
end
class Tp < ActiveRecord::Base
has_many :walks
has_many :cats, :through => :walks
end
Now, the relationship is similar to a has_and_belongs_to_many, but you can include metadata about the walking relationship. Additionally, say that a Tp also walks dogs. You could convert the belongs_to :cat into a polymorphic belongs_to :animal relationship so that a Tp can walk a cat, dog, mouse, rabbit, horse, ... you name it.
class Walk < ActiveRecord::Base
belongs_to :animal, :polymorphic => true
belongs_to :tp
attr_accessible :price, :duration, :interval # these attributes describe the Walk relationship
end
class Cat < ActiveRecord::Base
has_many :walks, :as => :animal
has_many :tps, :through => :walks
end
class Dog < ActiveRecord::Base
has_many :walks, :as => :animal
has_many :tps, :through => :walks
end
class Tp < ActiveRecord::Base
has_many :walks
has_many :cats, :through => :walks, :source => :animal, :source_type => 'Cat'
has_many :dogs, :through => :walks, :source => :animal, :source_type => 'Dog'
end
This relationship is created with a migration like
class CreateWalks < ActiveRecord::Migration
def change
create_table :walks do |t|
t.belongs_to :animal, :polymorphic => true
t.belongs_to :tp
end
end
end