# Joining three objects uniquely using :has\_many

**URL:** <https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003>\
**Category:** Rails\
**Created:** [February 22, 2009, 1:20am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003 "2009-02-22T01:20:39Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 1:20am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/1 "2009-02-22T01:20:39Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 1:27am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/2 "2009-02-22T01:27:36Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 1:32am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/3 "2009-02-22T01:32:16Z")

</div>

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

- id (PK)
- level\_id (FK)
- product\_id (FK)
- part\_id (FK)

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 2:26am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/4 "2009-02-22T02:26:03Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 1:42am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/5 "2009-02-22T01:42:12Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 24, 2009, 1:09am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/6 "2009-02-24T01:09:30Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 25, 2009, 7:25pm UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/7 "2009-02-25T19:25:12Z")

</div>

Andrew,

Changing the scope of validates\_uniqueness\_of to product\_id solved my  
problem. Thank you.

Evan

---

<div class="post-metadata">

**Author:** ![evan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/e/54ee81/32.png) [@evan](https://www.ruby-forum.com/u/evan)\
**Post date:** [February 22, 2009, 5:33am UTC](https://www.ruby-forum.com/t/joining-three-objects-uniquely-using-has-many/161003/8 "2009-02-22T05:33:11Z")

</div>

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.
