Joining three objects uniquely using :has_many


#1

I’ve confused myself.

I have three objects: level, product, part. Together they classify a
repair type.

  • Replacing a part in a product has a level of difficulty associated
    with it.
  • The same part replaced in a different product has a different level
    of difficulty.

How can I model an association among these?

I have created a join model, “Repair class,” and connected all three
using :has_many through. However, this does not prevent multiple
records where the part and product stay the same, but the level
changes. In other words, I end up with multiple levels assigned to the
same part/product combination which does not happen in the real world.

What’s the best way to proceed?

Evan


#2

On 22 Feb 2009, at 00:20, Evan wrote:

How can I model an association among these?

I have created a join model, “Repair class,” and connected all three
using :has_many through. However, this does not prevent multiple
records where the part and product stay the same, but the level
changes. In other words, I end up with multiple levels assigned to the
same part/product combination which does not happen in the real world.

You might want to describe exactly what join model you have created

Personally my instinct would be to have a unique index on the join
table enforcing that there is only one entry for each part/product pair

Fred


#3

My join model is “Repair class.” Its attributes are:

  • id (PK)
  • level_id (FK)
  • product_id (FK)
  • part_id (FK)

#4

I just tried this code and was still able to create records with
different levels for the same part/product combination.

class RepairClass < ActiveRecord::Base
belongs_to :level
belongs_to :product
belongs_to :part

validates_presence_of :level_id, :product_id, :part_id
validates_uniqueness_of :level_id, :scope =>
[ :product_id, :part_id ]
end


#5

So I could create a unique index consisting of product_id, part_id. Is
it possible to then create a unique index consisting of level_id and
the other index? I am using MySQL currently.


#6

Andrew,

Your last paragraph helps clarify it a little bit. I think you are
correct in that I am chasing the wrong thing. I think that stems from
not fully understanding the scope option of validates_uniqueness_of.
The documentation for it is not clear.

Evan


#7

Andrew,

Changing the scope of validates_uniqueness_of to product_id solved my
problem. Thank you.

Evan


#8

I’m not sure I’m 100% clear, but it sounds like you are saying that a
Repair may only have one combination of Part/Product, and it just so
happens that this combination implies a Level. To me the validation
would be:

class Repair < ActiveRecord::Base
belongs_to :level
belongs_to :product
belongs_to :part

validates_presence_of :level_id, :product_id, :part_id
validates_uniqueness_of :part_id, :scope => [:product_id]
end

Assuming I understand the problem, I think your confusion stems from
the fact that the level isn’t part of what defines a Repair, its
simply an attribute of the Repair. It is really the Part and the
Product that you should be concerned with.