idbentley
idbentley

Reputation: 4218

belongs_to a set of classes

I have a situation that I am struggling with conceptually. I have a class Media, which represents (unsurprisingly) a peice of media. Now, there are many several different classes that own Media - for example, a Question can own a Media object, an Answer can also own a Media object.

Trying to model this from the direction of the owning class is easy, I just give them the appropriate has_one or has_many associations. However, I am struggling in the other direction. A media does not belong_to any particular class, it can belong to any of a set of classes.

I suspect that the appropriate thing to do in these moments of ambiguity, is to simply NOT annotate the class with any belongs_to association. However, I can't find any documentation confirming this.

Upvotes: 2

Views: 217

Answers (3)

Marek Příhoda
Marek Příhoda

Reputation: 11198

It should be noticed that nothing really prevents you from doing this:

class Media < ActiveRecord::Base
  belongs_to :question
  belongs_to :answer
end

However, the biggest disadvantage of this approach is that in your media table you would have to have two nullable foreign key columns - question_id and answer_id.

And if you wanted to attach the media to another class, you would have to add another nullable foreign key column to the media table.

To solve this problem, Rails has polymorphic associations for you - using them, the media can belong to many other models, all through a single association.

So now you can model your associations as in @Sean Hill's answer to your question:

class Media < ActiveRecord::Base
  belongs_to :mediable, :polymorphic => true
end

class Question < ActiveRecord::Base
  has_many :media, :as => :mediable
end

# etc.

Your media table would represent the polymorphic association with just one pair of columns: mediable_id (some integer) and mediable_type ('Question' or 'Answer').

Don't forget that you will also need to add the columns to the relevant migration (the CreateMedia migration - adjust as needed):

t.integer :mediable_id
t.string  :mediable_type

Or, more simply, with just one column:

t.references :mediable, :polymorphic => true

Upvotes: 1

Sean Hill
Sean Hill

Reputation: 15056

What you're looking for is a polymorphic relationship.

class Media < ActiveRecord::Base
  belongs_to :mediable, :polymorphic => true
end

class Question < ActiveRecord::Base
  has_many :media, :as => :mediable
end

class Answer < ActiveRecord::Base
  has_many :media, :as => :mediable
end

I answered a similar question where the same media could belong to more than one model. You may find that answer useful. https://stackoverflow.com/a/8237597/367611

Upvotes: 2

coreyward
coreyward

Reputation: 80140

What you're doing is a textbook polymorphic association. The guides has a good bit on them.

http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

A slightly more advanced twist on associations is the polymorphic association. With polymorphic associations, a model can belong to more than one other model, on a single association. For example, you might have a picture model that belongs to either an employee model or a product model.

Upvotes: 1

Related Questions