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).