# Has through

**URL:** <https://www.ruby-forum.com/t/has-through/86599>\
**Category:** Rails\
**Created:** [February 23, 2007, 4:50pm UTC](https://www.ruby-forum.com/t/has-through/86599 "2007-02-23T16:50:06Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 23, 2007, 4:50pm UTC](https://www.ruby-forum.com/t/has-through/86599/1 "2007-02-23T16:50:06Z")

</div>

Hi,

I have 3 models

Product  
has\_many :mutations

def current\_stock  
  
end

Mutation  
belongs\_to :product  
belongs\_to :location

Location  
has\_many :mutations

Question:

I can show the currect stock of a product with

product.current\_stock

but how do a walk through all locations and show the different current  
stock of per location. I was thinkinf of using has many through and  
the use something like product.current\_stock.locations.each etc.

But ofcourse that does not work.

Who can help ?

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 23, 2007, 5:03pm UTC](https://www.ruby-forum.com/t/has-through/86599/2 "2007-02-23T17:03:10Z")

</div>

On 2/23/07, bitterbal [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> But ofcourse that does not work.
> 
> Who can help ?

Correct me if I’m wrong, but is this what you’re looking for?

Product  
has\_many :locations, :through =\> :mutations  
end

Mutations  
belongs\_to :product  
belongs\_to :location  
end

Location  
has\_many :products, :through =\> :mutations  
end

product\_stocks = Location.products.collect {|x| x.current\_stock}

If you need the name you could do something like

Location.products.each { |product| products[product.name] =  
product.current\_stock }

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 23, 2007, 6:54pm UTC](https://www.ruby-forum.com/t/has-through/86599/3 "2007-02-23T18:54:27Z")

</div>

maybe a newbie question but what if I have a product ID and want to  
show current\_stock in the different locations?

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 23, 2007, 7:35pm UTC](https://www.ruby-forum.com/t/has-through/86599/4 "2007-02-23T19:35:31Z")

</div>

First of all, I’m an idiot because I didn’t include something necessary  
to  
make it work  
Both Product and Location should have has\_many :mutations

Secondly, what you’re asking means you want to do something other than  
what  
you had in your original post: you want to have a current\_stock for each  
product \<-\> location relationship… that means current\_stock should be  
part  
of the Mutation model.

If you want to keep a total current\_stock for each product, and a  
seperate  
one for each product at each location, then you would want to have  
current\_stock in Location be the sum of the stocks for all the  
locations,  
which means your code should now look like:

class Location \< ActiveRecord::Base  
has\_many :mutations  
has\_many :locations, :through =\> :mutations  
def current\_stock  
Mutations.find\_by\_product\_id(self.id).inject(0) {|total,  
location\_stock|  
total += location\_stock.current\_stock}  
end  
end

class Mutation \< ActiveRecord::Base  
belongs\_to :location  
belongs\_to :product  
def current\_stock  
# code to figure out current stock for this product/location  
end  
end

class Location \< ActiveRecord::Base  
has\_many :mutations  
has\_many :products, :through =\> :mutations  
end

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 25, 2007, 9:52am UTC](https://www.ruby-forum.com/t/has-through/86599/5 "2007-02-25T09:52:19Z")

</div>

I guess I am not being clear on what I am looking for 🙂

What I want is for a product X (example: 24710089) to show the  
current\_stock in all locations. Show I should get some array/hash that  
allows me to output

Product 24710089  
Location A: 10  
Location C: 20  
etc.

Based on examples above this is what I have implemented now:

class Product \< ActiveRecord::Base  
has\_many :mutations  
has\_many :locations, :through =\> :mutations

def current\_stock  
Mutation.find\_all\_by\_product\_id(self.id).inject(0) {|total, mut|  
total += mut.mutation}  
end  
end

class Mutation \< ActiveRecord::Base  
belongs\_to :product  
belongs\_to :location

def mutation  
mutation = 0 + self.buy - self.sell - self.transit - self.lost  
end  
end

class Location \< ActiveRecord::Base  
has\_many :mutations  
has\_many :products, :through =\> :mutations

def current\_stock  
Mutation.find\_all\_by\_location\_id(self.id).inject(0) {|total,  
mut| total += mut.mutation}  
end

end

So the solution I am looking for should allow me to input the  
product\_id as the selection criteria then return all locations with  
the current\_stock value. Any tips ?

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 26, 2007, 7:58pm UTC](https://www.ruby-forum.com/t/has-through/86599/6 "2007-02-26T19:58:44Z")

</div>

On 2/25/07, bitterbal [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> Location C: 20
> 
> ```
> 
> etc.

Ahh… I thought you wanted to be able to have stock at each location,  
and  
then have the total stock (which is what product.current\_stock does  
right  
now). I’m going to go ahead and leave that alone, and show you another  
function that will do what you want (that way you can do either one)

class Product \< ActiveRecord::Base

> has\_many :mutations  
> has\_many :locations, :through =\> :mutations
> 
> def current\_stock  
> Mutation.find\_all\_by\_product\_id(self.id).inject(0) {|total, mut|  
> total += mut.mutation}  
> end  
> end

def location\_stocks  
Mutation.find\_all\_by\_product\_id(self.id).each {|m| location\_stock[  
m.location\_id] = m.mutation}  
location\_stock  
end

Now let’s say you have two locations, location1 and location2.  
location1’s id = 14839, current stock = 24  
location2’s id = 19248, current stock = 94  
If each of those locations had product 24710089 in it, and you did  
l = Product.find(24710089).location\_stocks  
then  
l[14839] == 24  
l[19248] == 94  
Or, in Ruby parlance  
l == {14839 =\> 24, 19248 =\> 94}

Will that do what you want?

class Mutation \< ActiveRecord::Base

> has\_many :products, :through =\> :mutations  
> You probably don’t want current\_stock here, unless you want it to show  
> the  
> stock of all the products at this location  
> If you do, it’s:  
> def current\_stock  
> Mutation.find\_all\_by\_location\_id(self.id).each {|m| product\_stock[  
> m.product\_id] = m.mutation}  
> product\_stock  
> end  
> This will be  
> product\_stock == {24710089 =\> 24} for location1  
> and  
> product\_stock == {24710089 =\> 94} for location2

end

---

<div class="post-metadata">

**Author:** ![bitterbal](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/838e76/32.png) [@bitterbal](https://www.ruby-forum.com/u/bitterbal)\
**Post date:** [February 23, 2007, 7:49pm UTC](https://www.ruby-forum.com/t/has-through/86599/7 "2007-02-23T19:49:35Z")

</div>

And if I weren’t an idiot, this would have said class Product instead of  
class Location at the top

class Location \< ActiveRecord::Base
