Getting the associated model in a join table

Let’s say we have the following models:

Product < ActiveRecord::Base
has_many :products_suppliers
has_many :suppliers, :through => :products_supplier

Supplier < ActiveRecord::Base
has_many :products_suppliers
has_many :products, :through => :products_supplier

ProductsSupplier < ActiveRecord::Base
belongs_to :supplier
belongs_to :product
end

Let’ say we have this data in the products_suppliers table:

supplier_id | product_id |
1 | 2 |
1 | 1 |
2 | 2 |
2 | 1 |

So when I do this:

@suppliers = Product.find(1).suppliers, it will return an array with the suppliers with id 1 and 2.

Let’s say I do this:

@supplier = @suppliers[0] #Which is the supplier with id = 1, the one fetched from the (supplier_id = 1, product_id = 2) record in the products_suppliers table. Ok, now I got a single instance of a supplier. What I want is to get the product_id it was associated with in the products_suppliers table. (product_id = 2), might be missing something simple, but I can’t seem to find a way to do that.

Any help appreciated!

Thanks,

Marcelo.

Actually the question would be better put this way:

Having a has_one association via a link table (in this case products_supplier).

Thanks.

I couldn't really get your question, but I think that what you are
trying to do here is to create a many to many relationship.

Rails actually provides the has_and_belongs_to_many association for
this very purpose, which should help you with what you are asking (I
think).

http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many

Hello Jaryl, thanks for the reply,

habtm is equivalent to has_many :through. just that the join table is not an entity by itself, this I know :slight_smile:

So, I will try to explain through an example:

Let’s say we have a products table:

id | name | price |
1 | a | 2.50 |
2 | b | 1.50 |

A suppliers table

id | name |
1 | s_a |
2 | s_b |

So the idea is: A product may be supplied by many different suppliers. So, indeed, it is a has_and_belongs_to_many relationship. The thing is, the relationship by itself defines the type of the supplier (if it’s either a vendor or a manufacturer), since we have:

products_suppliers table:

id | supplier_id | product_id | supplier_type |
1 | 1 | 1 | “vendor” |
2 | 1 | 2 | “manufacturer”|

So, in tis case, supplier s_a is a “vendor” of product a and a “manufacturer” of product b.

Product < ActiveRecord::Base
has_many :products_suppliers
has_many :suppliers, :through => :products_suppliers
end

Supplier < ActiveRecord::Base
has_many :products_suppliers
has_many :products, :through => :products_suppliers
end

ProductsSupplier < ActiveRecord::Base
belongs_to :product
belongs_to :supplier
end

So let’s take this code

p = Product.find(1)

What I need is a specific products_suppliers related to specific combo of (supplier_id,product_id) keys, so I can get the supplier_type data related to this product+supplier. However, I the has_many relationship in Supplier reaturns all the products_suppliers that has supplier_id == the id of this supplier – To get the specific products_supplier I would also need the id of the product that owns this supplier, but I can’t get it. So, the first thing I though was something like has_one :through. That’s why I said, a has_one relationship through a link table. The thing is, I need the product_id that owns this supplier from the supplier model. I might as well just leave AR and do some raw SQL, or maybe use sql :select statemente in the association to fetch the type from the link table into the association.

Does that make sense?

Thanks,

Marcelo.

Hmm, maybe I’m complicating things. Maybe a delegate to the Product class (to get the product_id) would solve this… hmm, I will try that and let you guys know. Well, amazing how the fact of writing to the mailing list helps to find the solution by yourself :slight_smile:

Thanks!

Marcelo.

Maybe this is too obvious, but given the example above, couldn't you
just use:

p.products_suppliers.find_by_supplier_id(some_supplier_id)

That will give you the supplier that you're looking for.

BTW, I'd recommend a different name for the supplier_type column -
that pattern (same as the foreign key, with _type instead of _id) is
the Rails convention for single table inheritance, which is NOT what
you're looking for here. It shouldn't cause a problem, but the AR
association code is known to sometimes get really weird indigestion
from that situation...

Hope this helps,

--Matt Jones

I don't think a delegate would work. Okay, just to recap on the
database structure:

products

id | name | price |
1 | a | 2.50 |
2 | b | 1.50 |

suppliers

id | name |
1 | s_a |
2 | s_b |

products_suppliers

id | supplier_id | product_id | supplier_type |
1 | 1 | 1 | "vendor" |
2 | 1 | 2 | "manufacturer"|

Firstly, I don't think that 'products_suppliers' is a good name since
this form is only a requirement for HABTM to work. It should be more
descriptive of the relationship, but your call.

Okay, so I believe that you want to do something like this:

p = Product.first

p.vendors # => [#< Supplier id: 1, name: "s_a">, #<Supplier id: 2,
name: "s_b">]
p.manufacturer # => #< Supplier id: 1, name: "s_a">

You might want to try this:

class Product < ActiveRecord::Base
  has_many :products_suppliers
  has_many suppliers, :through => products_suppliers

  named_scope :vendors, :conditions => ["supplier_type = ?", "vendor"]
  named_scope :manufacturer, :conditions => ["supplier_type = ?",
"manufacturer"]
end

This does not restrict the manufacturer to a one-to-many relationship,
so you will have to ensure that each product will have only one
manufacturer (you can do this pretty easily with some validations).