ActiveRecord Associations Help

I’m new to rails and I’m creating an e-commerce site for a personal/learning project. Learning about associations, how would I model a scenario where Users have many Orders and Orders have many Products?

The answer seems pretty contained in your question!

Something like:

class User < ApplicationRecord
  has_many :orders
end

class Order < ApplicationRecord
  has_many :products
  belongs_to :user
end

class Product < ApplicationRecord
  belongs_to :order
end

However! My experience building ecommerce sites is that that exact approach will get you into trouble. You probably actually want a has-and-belongs-to-many relationship between Orders and Products. Here’s why:

A relationship where an Order has many Products implies that each Product will have only one Order. So if User A and User B both try to order Hello Kitty notebooks, too bad so sad. Whoever gets there first will purchase the only notebook available for sale, and the other person will be left out in the cold.

With a has-and-belongs-to-many relationship, we create a “join model” that belongs to both Product and Order. That way, Products can have many Orders through that join model, and vice versa. So multiple users can order their Hello Kitty notebooks and have things turn out fine.

Rails gives you two ways to model a has-and-belongs-to-many relationship. The one I’d recommend would be using a explicit join model with has_many through:. To do this, you’d create a model to sit “in between” Order and Product. OrderProduct would be the name a lot of devs would default to. I’ve also seen Lineitem or OrderLineitem used for this exact relationship in a lot of ecommerce applications. When it’s possible to think of a domain-relevant name for your join model (like Lineitem), it’s often healthy for your application down the line when that model inevitably takes on more responsibilities.

What this would look like:

class Order < ApplicationRecord
  has_many :lineitems
  has_many :products, through: :lineitems
end

class Product < ApplicationRecord
  has_many :lineitems
  has_many :orders, through: :lineitems
end

class Lineitem < ApplicationRecord
  belongs_to :order
  belongs_to :product
end

Whichever way you go, don’t forget to use the t.references method in your migrations to add foreign key columns on models at the belongs_to end of any association.

Hope this helps!

2 Likes

Thanks a lot. This is why I asked because I was learning about has_many through: and wasn’t too sure when/where I’d use it. Your explanation clarified it.