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:
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.
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:
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| -%>
<%= each.name %> <%= each.price %>
<%= each.quantity %>, Total: <%= each.quantity * each.price %>
<% end -%>