Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to have "polymorphic has_one" relationship in rails?

I'd like to do something like this:

Category -------- - id - name  Tag -------- - id - tag   Campaign -------- - id - name - target (either a tag *or* a category) 

Is a polymorphic association the answer here? I can't seem to figure out how to use it with has_one :target, :as => :targetable.

Basically, I want Campaign.target to be set to a Tag or a Category (or potentially another model in the future).

like image 442
markquezada Avatar asked Sep 08 '11 01:09

markquezada


People also ask

What is a polymorphic relationship rails?

Polymorphic relationship in Rails refers to a type of Active Record association. This concept is used to attach a model to another model that can be of a different type by only having to define one association.

What is polymorphic Ruby?

In Ruby on Rails, a polymorphic association is an Active Record association that can connect a model to multiple other models. For example, we can use a single association to connect the Review model with the Event and Restaurant models, allowing us to connect a review with either an event or a restaurant.


1 Answers

I don't believe you're in need of a has_one association here, the belongs_to should be what you're looking for.

In this case, you'd want a target_id and target_type column on your Campaign table, you can create these in a rake with a t.references :target call (where t is the table variable).

class Campaign < ActiveRecord::Base   belongs_to :target, :polymorphic => true end 

Now campaign can be associated to either a Tag or Category and @campaign.target would return the appropriate one.

The has_one association would be used if you have a foreign key on the target table pointing back to your Campaign.

For example, your tables would have

Tag: id, tag, campaign_id Category: id, category, campaign_id

and would have a belongs_to :campaign association on both of them. In this case, you'd have to use has_one :tag and has_one :category, but you couldn't use a generic target at this point.

Does that make more sense?

EDIT

Since target_id and target_type are effectively foreign keys to another table, your Campaign belongs to one of them. I can see your confusion with the wording because logically the Campaign is the container. I guess you can think of it as Campaign has a single target, and that's a Tag or a Container, therefore it belongs in a Tag or Container.

The has_one is the way of saying the relationship is defined on the target class. For example, a Tag would have be associated to the campaign through a has_one relationship since there's nothing on the tag class that identifies the association. In this case, you'd have

class Tag < ActiveRecord::Base   has_one :campaign, :as => :target end 

and likewise for a Category. Here, the :as keyword is telling rails which association relates back to this Tag. Rails doesn't know how to figure this out upfront because there's no association with the name tag on the Campaign.

The other two options that may provide further confusion are the source and source_type options. These are only used in :through relationships, where you're actually joining the association through another table. The docs probably describe it better, but the source defines the association name, and source_type is used where that association is polymorphic. They only need to be used when the target association (on the :through class) has a name that isn't obvious -- like the case above with target andTag -- and we need to tell rails which one to use.

like image 159
Kristian PD Avatar answered Sep 28 '22 07:09

Kristian PD