The other day, I was developing an ordering system. In this system, an order has many items and each item has a price. I also needed to store how many of each item was being purchased. So I used my old friend habtm, and noticed that a simple join table wouldn’t be enough, I would need to store a quantity for each item sold. From the Rails docs:
So I used a join model, but kept to the Rails naming convention for join tables and script/generated a model called ‘items_orders’, and my three models looked something like this:Deprecated: Any additional fields added to the join table will be placed as attributes when pulling records out through has_and_belongs_to_many associations. Records returned from join tables with additional attributes will be marked as ReadOnly (because we can‘t save changes to the additional attrbutes). It‘s strongly recommended that you upgrade any associations with attributes to a real join model.
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class ItemsOrders < ActiveRecord::Base
belongs_to :item
belongs_to :order
validates_presence_of :quantity
end
From my ItemsOrders join model, I can access the quantity for each item. From script/console:
>> item = Item.create :price => 100, :name => 'Giant robot'
>> order = Order.create
>> items_orders = ItemsOrders.create :item => item, :order => order, :quantity => 10
>> items_orders = ItemsOrders.create :item => item, :order => order, :quantity => 5
>> order.items.collect{|each| each.quantity}
=> ["10", "5"]
>> item.quantity
NoMethodError: undefined method `quantity' for #<Item:0xb777aa30>
You’ll notice that item itself does not understand quantity, but when accessed through an order with a habtm association it does. This allows me more access to order specific information, so later in the view I can do things like this:
<h1>Order summary</h1>
<% @order.items.each do |each| -%>
<p>
<%= each.name %> <%= each.price %>
<%= each.quantity %>, Total: <%= each.quantity * each.price %>
</p>
<% end -%>