# Has\_many :through query question

**URL:** <https://www.ruby-forum.com/t/has-many-through-query-question/83490>\
**Category:** Rails\
**Created:** [January 26, 2007, 4:59pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490 "2007-01-26T16:59:52Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 4:59pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/1 "2007-01-26T16:59:52Z")

</div>

I think this is obvious. but for some reason I’m not getting it.

Models: Bicycles, Accessories  
Join Model: Upgrades

The idea is that you can upgrade your bike by adding an accessory, and  
the upgrade price is often less than the accessory price would normally  
be. So the Upgrades table has id, bicycle\_id, accessory\_id, and price.

class Bicycle  
has\_many :upgrades  
has\_many :accessories, :through =\> :upgrades  
end

class Upgrade  
belongs\_to :bicycle  
belongs\_to :accessory

# price defined in table

end

Now my question is, I need to query the upgrades for a bicycle for a  
specific accessory.

I was doing this:

accessory = Accessory.find\_by\_description(“headlight”)  
bike.upgrades.find\_by\_accessory\_id(accessory.id)

but it seems klunky. Is there a better way to perform this kind of  
lookup?

Thanks  
Jeff

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 5:12pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/2 "2007-01-26T17:12:57Z")

</div>

Hi Jeff,

So if I am tracking with you here, you are looking for Accessories  
associated with a Bicycle as joined by the Upgrades. You should be  
able to just call bicycle.accessories.find(accessory) if you have an  
accessory, since Accessories are now associated with the Bicycle object  
via the has\_many :though declaration. The has\_many :through takes care  
of the need to go to the Upgrades object first from Bicycles since you  
specified the relationship in the model.

Hope this helps!

C

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 5:10pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/3 "2007-01-26T17:10:55Z")

</div>

It seems as bikes can have accessories that may or may not be upgrades,  
so:

bike.upgrades.accessories.find\_by\_description(“headlight”)

Otherwise, if there are only upgrade accessories, you can bypass a step:

bike.accessories.find\_by\_description(“headlight”)

Though in terms of good design (no dependency chaining), it might be a  
good  
idea to add a method to one of the models to hide this away. Say,  
Updgrade#find\_accessory\_by\_description

Jason

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 5:24pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/4 "2007-01-26T17:24:58Z")

</div>

On Jan 26, 10:12 am, “redmotive” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> So if I am tracking with you here, you are looking for Accessories  
> associated with a Bicycle as joined by the Upgrades. You should be  
> able to just call bicycle.accessories.find(accessory) if you have an  
> accessory, since Accessories are now associated with the Bicycle object  
> via the has\_many :though declaration. The has\_many :through takes care  
> of the need to go to the Upgrades object first from Bicycles since you  
> specified the relationship in the model.

Almost but not quite - what I need is the Upgrade row, not the  
Accessory row, because I need to know the price for the upgrade given  
the desired accessory.

So bicycle.accessories.find\_by\_description(“headlight”) returns an  
Accessory object, but I still don’t know the upgrade price.

That’s why this is a bit tricky - I want an upgrade row, selected by  
the accessory description. Since the description is not in the join  
table, it seems I have to do two lookups - one to find the accessory,  
and the other to find the upgrade row given the accessory id (yuck).

Jeff

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 5:31pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/5 "2007-01-26T17:31:59Z")

</div>

On Jan 26, 10:14 am, Keynan P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> first toss the price and the id columns from your through table. you  
> never need id and it might be screwing some thing up. the total order  
> price should be calculated at run time and the accessory price should be  
> in the accessories table.

I have a real join model here, not a simple habtm, because an upgrade  
price is different from the normal accessory price. Sorry if I wasn’t  
clear about that.

(ID columns never “screw things up”, btw).

> def get\_accessory(upgrade\_type){  
> self.upgrades().each() do |up|  
> return up if up.description == “headlight”  
> end
> 
> }this should cut down your proccessing to as it eliminates a costly DB  
> query.(assuming there are not a huge amount of upgrades assigned to that  
> bike)

Actually, self.upgrades will result in a query against the database…  
plus you’ve got another performance hit by iterating through the  
objects (I would suggest a .detect here instead of .each, btw).

Thanks anyway  
Jeff

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 8:46pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/6 "2007-01-26T20:46:52Z")

</div>

It sounds like your model might need abstraction needs some work,  
although  
more information is needed to know what you are trying to accomplish.  
You  
might need to sit down and rethink the relationships between the models  
and  
what information should be in which table.

Why are you using an upgrades table? Are accessories available as  
non-upgrades? If an accessory is accessed as an upgrade is the  
information  
different than when as an accessory only? Is there a reason you don’t  
want  
to put the upgrade fields into your accessory model and bypass the  
has\_many  
:through association completely?

M\<\>\<

---

<div class="post-metadata">

**Author:** ![jeff](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/j/e19b73/32.png) [@jeff](https://www.ruby-forum.com/u/jeff)\
**Post date:** [January 26, 2007, 5:14pm UTC](https://www.ruby-forum.com/t/has-many-through-query-question/83490/7 "2007-01-26T17:14:37Z")

</div>

first toss the price and the id columns from your through table. you  
never need id and it might be screwing some thing up. the total order  
price should be calculated at run time and the accessory price should be  
in the accessories table.

second bicycle and accessories should both use has\_and\_belongs\_to\_many  
for the other with through =\> Upgrade(s).

now for your question. No this is specific problem so the best way to  
handle it is to add a method to your bike model in which you can pass in  
“headlight”.

def get\_accessory(upgrade\_type){  
self.upgrades().each() do |up|  
return up if up.description == “headlight”  
end  
}

this should cut down your proccessing to as it eliminates a costly DB  
query.(assuming there are not a huge amount of upgrades assigned to that  
bike)
