has_one :item, :polymorphic => true

I have a relationship were a person can have 1 item, and this item is either a A || B || C. Rails supports the belongs_to polymorphic, but I don't see how can a Model have one of several models as opposed to belong_to one of several models. Can anyone tell me how to map this relationship ?

Is this a one-to-one, or one-to-many relationship?

Which class should has the foreign key? Do your A, B, and C classes have a foreign key to person, or do you want person to have a foreign key to one of the other 3 models?

Yes I'd like person to have the foreign key to have a foreign key to one of the other 3 models.

dpal wrote in post #958489:

I have a relationship were a person can have 1 item, and this item is either a A || B || C. Rails supports the belongs_to polymorphic, but I don't see how can a Model have one of several models as opposed to belong_to one of several models. Can anyone tell me how to map this relationship ?

You want Person belongs_to Item. Remember, belongs_to is used for the table that contains the foreign key field.

Best,

Yup, then like Marnen said, Person should belong to Item:

  belongs_to :item, :polymorphic => true

In your migration, you can use:

  t.references :item, :polymorphic => true

Which will create item_type and item_id columns.

Then each class A, B, and C can have one person:

  has_one :person, :as => :item

Awesome thank you so much, I had come to that conclusion although it didn't seem natural. This is what my spec looks like, I think its correct:

Database Associations

What we want:

There are users, construction companies, real state companies and natural persons. A user controls one of the 3 entities(construction companie, real state company or natural person). And each of this entities has listings, and in the case of construction company also projects. The relationships are as followed:

1. user has_one :construction_company or :real_state_company or :natural_person

2. construction_company has_many ---> :projects which has_many ---> : listings

3. real_state company has_many ----> :listing

4. natural_person has_one----> :listings

How this looks in code:

We need to use polymorphic associations to be able to express this relationships, because for example listings belongs to all 3 type of entities, thus listings has to be polimorphic.

:listings belongs to-> projects, real_state_company, natural_person

We start by looking at what this 3 entities have in common, mainly that they are the sellers, so we will add to the databes a foreign key that lists the seller identification(seller_id) and which type of seller he is(construction company or real state company etc).

In rails this is accomplished by:

$rails generate model Listing …. seller_id:integer seller_type:string

This will ad a seller_id and seller_type to the migrations. Rails has a way of simplifying this by replacing the 2 keys with:

t.references :seller, :polymorphic => true which will create both keys for use.

After, inside the listing model then we can say that the listing belongs to a seller instead of explicitly saying which seller(company....) this is expressed using the following code

class Listing < ActiveRecord::Base

    belongs_to :seller, :polymorphic => true

end

Then we can add the relationship to each one of the entities using :as

class Real_State_Company < ActiveRecord::Base

    has_many :listings, :as => :seller

end

Until here we have taken care of the relationship were a listing belongs to any 3 entities(projects, real_state_company and natural_person). Now we need to look at the case were the construction company, real state company or natural user belongs to a User. Since this is the reverse, we start looking what does the user has, he has_one of the 3 things(company, real state company or natural_person), we'll call this entities, thus in rails we can say:

class User < ActiveRecord::Base

    has_one :entity, :polymorphic => :true

end

And then each of the entities will have:

class Real_State_Company < ActiveRecord::Base

    belongs_to :user, :as => :entity

end

We need to modify the user model with entity_id and entity_type, or:

t.references :entity, :polymorphic => true

Unfortunatelly has_one polymorphic is not supported, therefore we need to think in reverse.

class User < ActiveRecord::Base

   belongs_to :ownable, :polymorphic => :true

end

And then each of the entities will have:

class Real_State_Company < ActiveRecord::Base

    has_one :user, :as => :ownable

end

We need to modify the user model with entity_id and entity_type, or:

t.references :entity, :polymorphic => true

Then to get if the user owns a company or natural person do @user.ownable, similary to see which user owns a company @company.user.

That's because if the "belongs_to" is on the company models, it's no longer a polymorphic relationship. At that point it's just a regular one-to-one relationship, like the following example. Each of the companies has a foreign key to Person.

class RealStateCompany   belongs_to :user end

class ConstructionCompany   belongs_to :user end

class NaturalPerson   belongs_to :user end

class User   has_one :real_state_company   has_one :construction_company   has_one :natural_person end

This works. Reversing the relationship also works. Just depends on how you want to design it and where you want the foreign keys.

Now there is also the fact that user has to know what they own. If I go with the

belongs_to :ownable, polymorphic => true.

From user then user.ownable will either return company, real_state_company or natural user. I would have to do user.ownable_type to see which one and use a string comparisson.

If I go the other route:

  has_one :real_state_company   has_one :construction_company   has_one :natural_person

I can say,

if(user.construction_company != nil) {   company = user.company   redirect_to company } else if (user.real_state_company) { .... } etc.

Which one is cleaner, better design ?

Thanks -dan

dpal wrote in post #958568:

Now there is also the fact that user has to know what they own. If I go with the

belongs_to :ownable, polymorphic => true.

From user then user.ownable will either return company, real_state_company or natural user.

(I think you mean "real estate", not "real state".)

I would have to do user.ownable_type to see which one and use a string comparisson.

Or user.ownable.class and use a class comparison.

Or better yet, do neither and use object polymorphism to automatically dispatch. Remember, one of the nice things about OO development is that objects know how to respond to messages -- you often don't need conditionals.

If I go the other route:

  has_one :real_state_company   has_one :construction_company   has_one :natural_person

I can say,

if(user.construction_company != nil) {   company = user.company   redirect_to company } else if (user.real_state_company) { .... }

This isn't Java. Lose the braces, parens, and "!= nil" and use "elsif" instead of "else if".

etc.

Which one is cleaner, better design ?

The polymorphic design. It allows you to actually harness the object model to your advantage.

Thanks -dan

Best,

Awesome help, thanks everyone. Just finished implementing the polymorphic way, and the code looks really clean. Thanks -dan