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