Topic: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Hi,I'm starting with Rails and I'm developing an app which will sit over this existing model (http://imgur.com/ZuaCm). Although I might apply some changes in the model (legacy stuff, so that is my last option in fact), I'm finding it hard to implement it with Rail's Active Record.
I have Item and Property models (many-to-many relation), whose relation has a Value (1 item has several properties, and a property has a value for that item). Now, to complicate things a little more, an hierarchy of several value types was defined (I've only drawn 1 column in DECIMAL_VALUE and TEXT_VALUE tables, but there are more columns and more tables like those).
So, until now I've created an "ItemValuation", which will be the joint model for Item/Property/Value, and my ideia is to use "has/through" between items and properties. But I don't know how to design the Value part. The idea would be have the value stored in one of the child tables, and have it's ID in the joint table. I've also read about polymorphic associations, which might be used here in that hierarchy (not sure how to use this here).
Another doubt I have is, if keeping this model, which resources need to be nested (item -> property -> value?).

Can someone help here?

Last edited by imnotnot (2012-11-02 12:19:31)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Your diagram indicates that

class Property
   has_many item_property_values
   has_many items, :through => item_property_values

class ItemPropertyValue
  belongs_to property
  belongs_to item
  belongs_to value
 
class Value
  belongs_to item_property_value
  has_one value_text #Has one's indicate a need to denormalise the database to improve performance, storage and efficiency
  has_one value_decimal #As above

class Item
  has_many item_property_values
  has_many properties, :through => item_property_values

Is this what you wanted to know?

I see nothing that needs a polymorphic relationship but then your post is not very clear at all as to the exact nature of your issue

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Thaks jamesw,

I think you made it clear. I was trying to implement some hierarchy/inheritance there with Value and its childs TextValue/DecimalValue. However, I don't even need to call Value.all.
Regarding other point, in terms of nested resources, I will only need to nest Property inside Item right? When adding a property to a certain item, I'll specify the property value at the same time and store all the information in the relation table.

The polymorphic I was thinking about was to refer to DecimalValue/TextValue as a "valuable", and keep that "valuable_id" on ItemPropertyValue model to refer to the specific table automatically (possibly without the Value model). But perhaps i'm just  misunderstanding the usage of polymorphic associations.

Thanks!

Last edited by imnotnot (2012-10-24 06:47:49)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Regarding other point, in terms of nested resources, I will only need to nest Property inside Item right? When adding a property to a certain item, I'll specify the property value at the same time and store all the information in the relation table.

You don't really need nested resources unless you particularly want url paths like http://www.my_site/item/property. I assume you would have a selection list, maybe even a type ahead selection list to select the property when editing/creating the item in the item form so you would not need a nested url for this.
See http://railscasts.com/episodes?utf8=%E2 … rch=select for good examples of this.

The polymorphic I was thinking about was to refer to DecimalValue/TextValue as a "valuable", and keep that "valuable_id" on ItemPropertyValue model to refer to the specific table automatically (possibly without the Value model). But perhaps i'm just  misunderstanding the usage of polymorphic associations.

Hhhmmm That's definitely a candidate for a polymorphic relationship.

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

I will try to setup this polymorphic association also. Regarding nested resources I now understand I only need to define Properties and Items routes, as properties are created outside the item context.

Thanks!

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

I have implemented your suggestion plus the polymorphic relation. The polymorphic part is now the only thing that is causing me some problems.
My relation table looks like this:

class PropertyValuation < ActiveRecord::Base
  belongs_to :property
  belongs_to :item
  belongs_to :valuable, :polymorphic => true
end

Item model:

class Item < ActiveRecord::Base
  has_many :propertyValuations  
  has_many :properties, :through => :propertyValuations
end

Property model:

class Property < ActiveRecord::Base 
  has_many :propertyValuations  
  has_many :items, :through => :propertyValuations  
end

Then I setup my values tables like this:

class DecimalValue < ActiveRecord::Base
  has_one :propertyValuations, :as => :valuable
end

class TextValue < ActiveRecord::Base  
  has_one :propertyValuations, :as => :valuable
end

Now, I've managed to create a Property associated to an Item via:

@item = Item.new(params[:item])
@item.properties.build(:name => 'THE NAME', :value_type => 'DECIMAL', :unit => 'THE UNIT')

But now I don't know how to associate a value to the relation. I've tried this way but it didn't work:

value = DecimalValue.new(:value => 12345)
@item.propertyValuations.build(:valuable => value)

I also checked in Rails console and DecimalValue.new(:value => 12345) outputs this result:

DecimalValue.new(:value => 12345)
 => #<DecimalValue id: nil, value: 12345, property_valuation_id: nil, created_at: nil, updated_at: nil> 

What I'm I doing wrong here?

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

I also checked in Rails console and DecimalValue.new(:value => 12345) outputs this result:

DecimalValue.new(:value => 12345)
=> #<DecimalValue id: nil, value: 12345, property_valuation_id: nil, created_at: nil, updated_at: nil>

That's exactly what I would expect and is perfectly correct

But now I don't know how to associate a value to the relation. I've tried this way but it didn't work:

value = DecimalValue.new(:value => 12345)
@item.propertyValuations.build(:valuable => value)

What exctly did not work?

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

After those lines:

value = DecimalValue.new(:value => 12345)
@item.propertyValuations.build(:valuable => value)

and @item.save

Item and property were correctly created, as well as the relation on ItemValuations table. However, DecimalValue record wasn't created and, therefore, its Id isn't present on the relation.
Regarding DecimalValue.new on Rails console, is it normal the id = nil?

Last edited by imnotnot (2012-10-24 20:49:36)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

id, created_at and updated_at are only set once a record hits the database. In the case of ID's it's normally the relevant RDBMS database (primary_key auto_increment) that assigns the value and Rails (ActiveRecord) deals with the dates.
In a rails console
What does this get you?

@item = Item.new
value = DecimalValue.new(:value => 12345)
pv = @item.propertyValuations.build(:valuable => value)
pv.valuable.inspect

You really should write a test for this

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

In a Rails console it gets me this:

@item = Item.new
 => #<Item id: nil, name: nil, description: nil, price: nil, created_at: nil, updated_at: nil> 

value = DecimalValue.new(:value => 12345)
 => #<DecimalValue id: nil, value: 12345, property_valuation_id: nil, created_at: nil, updated_at: nil> 

pv = @item.propertyValuations.build(:valuable => value)
 => #<PropertyValuation id: nil, item_id: nil, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: nil, updated_at: nil> 

pv.valuable.inspect
 => "#<DecimalValue id: nil, value: 12345, property_valuation_id: nil, created_at: nil, updated_at: nil>"

If it helps, my migrations are like this:

CreateDecimalValues:

class CreateDecimalValues < ActiveRecord::Migration
  def change
    create_table :decimal_values do |t|
      t.decimal :value
      t.references :property_valuation

      t.timestamps
    end
  end
end
CreatePropertyValuations:

class CreatePropertyValuations < ActiveRecord::Migration
  def change
    create_table :property_valuations do |t|
      t.references :item
      t.references :property
      t.references :valuable, :polymorphic => true
      t.string :valuable_type

      t.timestamps
    end
    add_index :property_valuations, :item_id
    add_index :property_valuations, :property_id
    add_index :property_valuations, :valuable_id
  end
end

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

pv.valuable.inspect
=> "#<DecimalValue id: nil, value: 12345, property_valuation_id: nil, created_at: nil, updated_at: nil>"

That's worked to that point. It's exactly waht I expected to see.
So run that same code again but this time save to the db and read back, checking at each stage

@item = Item.new
value = DecimalValue.new(:value => 12345)
pv = @item.propertyValuations.build(:valuable => value)
pv.valuable.inspect
@item.save
pv = @item.propertyValuations.first
pv.valuable.inspect
id = @item.id
read_item = Item.find(id)
pv = read_item.propertyValuations.first
pv.valuable.inspect

Show me the output from the inspect's and the sql generated from the save.

Also run

rake test

from a command prompt in the root folder of your app and show me the results

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Here's the results:

@item = Item.new
 => #<Item id: nil, name: nil, description: nil, price: nil, created_at: nil, updated_at: nil> 

value = DecimalValue.new(:value => 12345)
 => #<DecimalValue id: nil, value: 12345, propertyValuation_id: nil, created_at: nil, updated_at: nil> 

pv = @item.propertyValuations.build(:valuable => value)
 => #<PropertyValuation id: nil, item_id: nil, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: nil, updated_at: nil> 

pv.valuable.inspect
 => "#<DecimalValue id: nil, value: 12345, propertyValuation_id: nil, created_at: nil, updated_at: nil>" 

@item.save
   (0.3ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `items` (`created_at`, `description`, `name`, `price`, `updated_at`) VALUES ('2012-10-25 02:45:33', NULL, NULL, NULL, '20
12-10-25 02:45:33')
  SQL (0.2ms)  INSERT INTO `property_valuations` (`created_at`, `item_id`, `property_id`, `updated_at`, `valuable_id`, `valuable_type`) VALUES ('201
2-10-25 02:45:33', 3, NULL, '2012-10-25 02:45:33', NULL, NULL)
   (0.8ms)  COMMIT
 => true 

pv = @item.propertyValuations.first
  PropertyValuation Load (0.6ms)  SELECT `property_valuations`.* FROM `property_valuations` WHERE `property_valuations`.`item_id` = 3 LIMIT 1
 => #<PropertyValuation id: 5, item_id: 3, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: "2012-10-25 02:45:33", updated_at: "2
012-10-25 02:45:33"> 

pv.valuable.inspect
 => "nil" 

id = @item.id
 => 3 

read_item = Item.find(id)
  Item Load (0.4ms)  SELECT `items`.* FROM `items` WHERE `items`.`id` = 3 LIMIT 1
 => #<Item id: 3, name: nil, description: nil, price: nil, created_at: "2012-10-25 02:45:33", updated_at: "2012-10-25 02:45:33"> 

pv = read_item.propertyValuations.first
  PropertyValuation Load (0.7ms)  SELECT `property_valuations`.* FROM `property_valuations` WHERE `property_valuations`.`item_id` = 3 LIMIT 1
 => #<PropertyValuation id: 5, item_id: 3, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: "2012-10-25 02:45:33", updated_at: "2
012-10-25 02:45:33"> 

pv.valuable.inspect
 => "nil" 

Regarding the "rake test" command, I don't have tests defined, so the output is just

0 tests, 0 assertions, 0 failures, 0 errors, 0 skips

Last edited by imnotnot (2012-10-24 22:54:03)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

I just spotted a problem
You have some code that does not follow rails conventions that really should be put straight relating to the propery valuation relationhips declared in the models. Change propertValuations to property_valuations
So

Item model:

class Item < ActiveRecord::Base
  has_many :propertyValuations 
  has_many :properties, :through => :propertyValuations
end

Property model:

class Property < ActiveRecord::Base
  has_many :propertyValuations 
  has_many :items, :through => :propertyValuations 
end

Then I setup my values tables like this:

class DecimalValue < ActiveRecord::Base
  has_one :propertyValuations, :as => :valuable
end

class TextValue < ActiveRecord::Base 
  has_one :propertyValuations, :as => :valuable
end

becomes

Item model:

class Item < ActiveRecord::Base
  has_many :property_valuations  
  has_many :properties, :through => :property_valuations
end

Property model:

class Property < ActiveRecord::Base 
  has_many :property_valuations  
  has_many :items, :through => :property_valuations  
end
class DecimalValue < ActiveRecord::Base
  has_one :property_valuation, :as => :valuable
end

class TextValue < ActiveRecord::Base  
  has_one :property_valuation, :as => :valuable
end

Also note the singular property_valuation declaration on the has_one.
Put that straight then see what happens.

How on earth did you end up with no tests defined? Rails generates them automatically for you?
Also, what's the definition for the property valuation class?

Last edited by jamesw (2012-10-25 00:16:27)

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

My property valuation class looks like this:

class PropertyValuation < ActiveRecord::Base
  belongs_to :property
  belongs_to :item
  belongs_to :valuable, :polymorphic => true
end

Last edited by imnotnot (2012-10-25 10:19:39)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

ok, that looks right.
Did those changes have any effect?
Could you try the following?

value = DecimalValue.new(:value => 12345)
pv = PropertyValuation.new
pv.valuable = value
pv.inspect
pv.valuable.inspect
pv.save
read_pv = PropertyValuation.find(pv.id)
read_pv.valuable.inspect

I'll try to find some time to code this up if that doesn't work

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Those changes had no effect. After executing that block of commands on Rails console I got:

value = DecimalValue.new(:value => 12345)
 => #<DecimalValue id: nil, value: 12345, propertyValuation_id: nil, created_at: nil, updated_at: nil> 

DecimalValue.all
  DecimalValue Load (0.7ms)  SELECT `decimal_values`.* FROM `decimal_values` 
 => [#<DecimalValue id: 1, value: nil, propertyValuation_id: nil, created_at: "2012-10-25 02:59:41", updated_at: "2012-10-25 02:
59:41">] 

pv = PropertyValuation.new
 => #<PropertyValuation id: nil, item_id: nil, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: nil, updated_
at: nil> 

pv.valuable = value
 => #<DecimalValue id: nil, value: 12345, propertyValuation_id: nil, created_at: nil, updated_at: nil> 

pv.inspect
 => "#<PropertyValuation id: nil, item_id: nil, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: nil, updated
_at: nil>" 

pv.valuable.inspect
 => "#<DecimalValue id: nil, value: 12345, propertyValuation_id: nil, created_at: nil, updated_at: nil>" 

pv.save
   (0.4ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `property_valuations` (`created_at`, `item_id`, `property_id`, `updated_at`, `valuable_id`, `valuable
_type`) VALUES ('2012-10-25 19:23:23', NULL, NULL, '2012-10-25 19:23:23', NULL, NULL)
   (2.2ms)  COMMIT
 => true 

read_pv = PropertyValuation.find(pv.id)
  PropertyValuation Load (0.5ms)  SELECT `property_valuations`.* FROM `property_valuations` WHERE `property_valuations`.`id` = 9
 LIMIT 1
 => #<PropertyValuation id: 9, item_id: nil, property_id: nil, valuable_id: nil, valuable_type: nil, created_at: "2012-10-25 19:
23:23", updated_at: "2012-10-25 19:23:23"> 

read_pv.valuable.inspect
 => "nil" 

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

ok, I'm up to my eyes in work right now. I'll code this up over the weekend and get back to you with a working solution

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Thanks a lot, I appreciate your help! smile

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Hi jamesw,

I think I just found the problem in my code. The way we were associating the value with property_valuation wasn't correct, so instead of:

value = DecimalValue.new(:value => 12345)
pv = PropertyValuation.new
pv.valuable = value

I found that associating the property_valuation to the value will work:

item.properties.push(property)
item.save

pv = PropertyValuation.where(:item_id => 1, :property_id => 1).first
value = DecimalValue.new(:value => 12345)
value.property_valuation = pv
value.save

Now, my only questions are:

- Is this the best approach, what could be improved?
- Can I avoid the need to save the item explicitly before the value?
- Seems that I can't get the valuable from a PropertyValuation object (it returns nil), even if the record on the database is ok, with valuable_id and valuable_type correctly filled.

Last edited by imnotnot (2012-10-28 21:26:49)

Re: [Solved] Best approach to implement model? (Many-to-Many, polymorphic)

Sorry, I meant to pst earlier. You have the foreign keys the wrong way round
value should contain the foreign key for property valuation.
Sort that out and your original approach will work.
Either that, or change the has_one to a belongs_to

What you want and what you need are too often not the same thing!
When your head is hurting from trying to solve a problem, stop standing on it. When you are the right way up you will see the problem differently and you just might find the solution.
(Quote by me 15th July 2009)