I have two tables. Items, and Vendors. Items are sold by Vendors. So Item belongs_to :vendor and Vendor has_many :items. That works fine.
However, Items are not always manufactured by the Vendors that sell them, but sometimes they are. So I have a new column in my Item table called “manufacturer_id”. Rather than generate a new model called Manufacturer that duplicates Vendor identically, I tried to do a complex has_many and belongs_to to define manufacturer.
See here:
class Item < ActiveRecord::Base
belongs_to :vendor
belongs_to :manufacturer, :class_name => "Vendor", :foreign_key => "manufacturer_id"
end
class Vendor < ActiveRecord::Base
has_many :items
has_many :manufactured_items, :class_name => "Item", :foreign_key => "manufacturer_id"
end
Populating manufacturer_id in the items table works as expected on Create commands:
Item.create(:manufacturer => Vendor.find_by_abbrev("INV"))
And I can even get the manufacturer as the operation
item.manufacturer
which returns:
<Vendor:0x007ff06684e398>
HOWEVER:
item.manufacturer.name
fails completely with a hard exeption and I get the error:
undefined method `name' for nil:NilClass
running
debug item.manufacturer
gives
--- !ruby/object:Vendor
attributes:
id: 181
name: Invitrogen
website: http://www.invitrogen.com/
created_at: 2012-01-08 01:39:07.486375000Z
updated_at: 2012-01-08 01:39:07.486375000Z
abbrev: INV
so item.manufacturer.name should return the name for that vendor object above, Vendor: 0x007ff06684e398.
What am I doing wrong here?
Also, once I get this working I’d like to be able to similarly call:
vendor.manufactured_items
to get all the items that have the manufacturer_id of that vendor. is there a straightforward way to do that too?
My last ditch effort may involve having to do:
manufacturer = Vendor.new(item.manufacturer)
But that seems totally wrong, and goes against the rails documentation here:
http://guides.rubyonrails.org/association_basics.html#self-joins
please help!
Okay, I’ve actually built a demo Rails 3.1 project for you and posted it on GitHub. I’ve included the console output in the
READMEfile to prove that calls likeitem.seller.nameanditem.manufacturer.namework, as well as round-trip calls likevendor.sold_items.first.manufacturer.name, which allow you to get the name of the manufacturer of the first sold item for a particular vendor, for example.I think the root of the thing, as you noted, is that a
vendorand amanufacturer, for all intents and purposes, are identical. For that reason I combined them simply into theVendorclass, and setup the foreign key relationships in such a way that it should work the way I think you want it to.In particular, you should pay attention to the README file, which has the console session output that I ran to show it working. You’ll also want to take a look at the two model classes and how their associations are defined, as well as the
spec/factories.rbfile for how it sets up the fake database data (I’ve included those below).In re-reading your question this morning, I’m not sure what you were doing wrong, but you can probably chalk it up to a subtle error in your associations somewhere. It’s probably a case of you being really close, but not quite there. 😀
Here’s some snipets from the code:
app/models/item.rb
app/models/vendor.rb
spec/factories.rb
lib/tasks/populator.rake