I have models for Order and Address and would like each instance of Order to have a shipping and billing address from the Address table. I am trying to figure out the best way to set this up but can't get it to work.
I tried:
class Address < ActiveRecord::Base
belongs_to :order
end
has_one :billing_address, -> { where type: 'billing' }, class_name: "Address"
has_one :shipping_address, -> { where type: 'shipping' }, class_name: "Address"
which does not work but hopefully should give a sense of what I'm trying to do. Any guidance would be greatly appreciated.
There are multiple approaches for this as follows -
Approach 1: Using builtin STI (Single Table Inheritance) mechanism.
class Order < ActiveRecord::Base
has_one :billing_address
has_one :shipping_address
end
class Address < ActiveRecord::Base
belongs_to :order
# make sure that there is an `order_id` field
# and `type` field on `addresses` table for STI
end
class BillingAddress < Address
end
class ShippingAddress < Address
end
Example:
order = Order.new
billing_address = BillingAddress.new(content: '123 doggy lane')
order.billing_addresss = billing_address
shipping_address = ShippingAddress.new(content: '123 doggy lane')
order.shipping_address = shipping_address
order.save!
Approach 2: Using custom STI (Single Table Inheritance) mechanism.
class Order < ActiveRecord::Base
has_one :billing_address
has_one :shipping_address
end
class Address < ActiveRecord::Base
belongs_to :order
# make sure that there is an `order_id` field
# and `address_type` field on `addresses` table for custom STI
# important note: make sure its `address_type`, not `type`
# if it is `type`, rails builtin STI kicks in
validates_inclusion_of :address_type, in: ['shipping', 'billing']
end
Example:
order = Order.new
billing_address = Address.new(address_type: 'billing', content: '123 doggy lane')
order.billing_addresss = billing_address
shipping_address = Address.new(address_type: 'shipping', content: '123 doggy lane')
order.shipping_address = shipping_address
order.save!
Approach 3: Storing the address information in the order
, instead of the other way around.
class Order < ActiveRecord::Base
belongs_to :billing_address, class_name: 'Address', foreign_key: 'billing_address_id'
belongs_to :shipping_address, class_name: 'Address', foreign_key: 'shipping_address_id'
# make sure that there are `billing_address_id` and `shipping_address_id`
# fields setup on the `orders` table
end
class Address < ActiveRecord::Base
has_many :billable_orders, class_name: 'Order', foreign_key: 'billing_address_id'
has_many :shippable_orders, class_name: 'Order', foreign_key: 'shipping_address_id'
end
Example:
order = Order.new
billing_address = Address.new(content: '123 doggy lane')
order.billing_addresss = billing_address
shipping_address = Address.new(content: '123 doggy lane')
order.shipping_address = shipping_address
order.save!
Here's how you can do what your asking
migrations
rails g migration CreateOrders
rails g migration CreateAddresses shipping_id:integer billing_id:integer content:string
rake db:migrate
classes
class Order < ActiveRecord::Base
has_one :shipping_address, class_name: 'Address', foreign_key: 'shipping_id'
has_one :billing_address, class_name: 'Address', foreign_key: 'billing_id'
end
class Address < ActiveRecord::Base
belongs_to :order
end
implementation
rails c
order = Order.create
address = Address.create(content: '123 doggy lane')
order.shipping_address = address
order.billing_address = address
order.save!
order.shipping_address
#=> #<Address id: 1, shipping_id: 1, billing_id: 1, content: "123 doggy lane">
order.billing_address
#=> #<Address id: 1, shipping_id: 1, billing_id: 1, content: "123 doggy lane">