curv
curv

Reputation: 3844

Rails association not working

I don't even know where to start to explain my problem, so please bear with me :( !

I have two models; Product and Category - Product has_one Category and Category belongs_to Product

Product model

id
name
description
brand
merchant
price
image
link
category


Category model

id
name
product_id


Ok so now I'm looping through an XML feed create products like so:

Product.create(:name => node.xpath("./text/name/text()").inner_text.downcase, 
               :description => node.xpath("./text/desc/text()").inner_text,
               :brand => node.xpath("./brand/text()").inner_text,
               :merchant => node.xpath("../@name").inner_text,
               :category => Category.find_by_name(node.xpath("./cat/text()").inner_text.downcase),
               :price => "£" + node.xpath("./price/btext()").inner_text)

This appears to run great, until I look at what it's produced in the database... Here's the output from both tables.

Products table
(abridged version, contains 10,000+ records)

id: 1
....
....
....
category: <---- empty for all 10,000+ records... err wtf? Surely should be a category ID or name

Categories table
(only contains 8 records, one for each category)

id: 1
name: socks
product_id: 10662 <---- err wtf?

id: 2
name: combinations
product_id: 10658 <---- err wtf?

id: 3
name: shoes
product_id: 9954 <---- err wtf?

id: 4
name: nightwear
product_id: 10653 <---- err wtf?

id: 5
name: hats
product_id: 10655 <---- err wtf?

id: 6
name: shorts
product_id: 10221 <---- err wtf?

id: 7
name: jeans
product_id: 9973 <---- err wtf?

id: 8
name: trainers
product_id: 8072 <---- err wtf?

To be honest I'm so confused, I'm not sure what I want to see anymore :-/

I just want the two tables to link together lol. Am I missing something fundamental here!?

Upvotes: 0

Views: 746

Answers (3)

Xavier Holt
Xavier Holt

Reputation: 14621

When you use has_one / belongs to, only one of the models ends up containing the id of the other. In your case, since Category belongs_to Product, the categories table needs a product_id column, where it'll store the ID of the product it belongs to.

But if Product has_one Category (this also applies to has_many), it doesn't need a category_id column - Rails knows that to find the category (c) for a given product (p), it can just look through the categories table for a category where product_id == p.id.

From the code you posted, it looks like Rails is doing what it should - even though the relationship is accessible from both models, only the model that has the belongs_to directive has any relationship data stored in its table. In fact, Rails is smart enough to know that when you specify the {:category => cat} option when creating a Product, it should actually change the categories table - and it does. That's why you're seeing product_ids in your categories table, and nothing in your products table (and the column products.category doesn't even need to exist - the relationship will work fine without it).

With that said, I suspect that a Product should be able to belong to more than one Category, and , likewise, a Category should be able to contain more than one Product. In that case, you'll want to use has_and_belongs_to_many in both models, and create the join table categories_products to connect them...

Hope this clears things up!

Upvotes: 1

Pan Thomakos
Pan Thomakos

Reputation: 34340

There are a couple of changes you need to make. Firstly, your model structure should look like this:

class Product
  belongs_to :category
end

class Category
  has_many :products
end

Secondly, your products table should have a category_id, not a category and your categories table should not have a product_id at all. Your code example should then run properly.

Upvotes: 1

Jordan
Jordan

Reputation: 1238

Your relationship is backwards. If you want a product to be in a single category you need:

Product
belongs_to :category

Category
has_many :products

Upvotes: 1

Related Questions