Hi - Very new to RoR. I have looked around at different strategies for handling shipping and billing addresses for an e-commerce applications. What I am trying to do is to have a user registration form where the user need to also fill out the default shipping address. Ultimately, I would like for the users to be able to add additional shipping address and billing addresses. I am sort of going down the path outlined by ryanb''s here and using Single Table Inheritance where the shipping address and billing address inherits from address class. Is this the best approach? ryanb suggested something along the lines below: Customer has_many :addresses has_many :orders Address belongs_to :customer Order belongs_to :customer belongs_to :billing_address #... belongs_to :shipping_address #... Is this an instance where I need to use both STI and polymorphic association? Can someone walk through an example of what is needed to do this or a strategy to accomplish this? Here is what I have so far for the registration form: ************************************************ <% title "User Registration" %> <div id="registration_leftcol"> <h1>Why Sign Up?</h1> <ul> <li>Faster checkout</li> <li>Track your orders</li> <li>View your order history</li> <li>Receive email updates</li> </ul> <p> <b>Already registered? <%= link_to ''Login here!'', login_path %></ b> </p> </div> <div id="registration_rightcol"> <h1>Your Info</h1> <% form_for(@user) do |f| %> <%= f.error_messages %> <%= render :partial => ''form'', :locals => { :f => f } %> <%= render :partial => "addresses/address", :collection => @user.addresses %> <p> <%= image_submit_tag("/images/Register Button.jpg") %> </p> <% end %> </div> ************************************************ I haven''t created the order.rb model yet, but ryanb did suggest making sure that... "The billing_address_id column would go in the orders table. Same with the shipping." Is this the only table in the database that would contain this column? Should they also exist in the address table or is this handled by Rails through the type column? All so very confusing. Here''s my current code but I''m certain I''m missing some key concepts here. I am providing some of the code below. Any help would be greatly appreciated. users_controller.rb ************************************************ class UsersController < ApplicationController # GET /users # GET /users.xml before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => [:show, :edit, :update] @title = ''User Registration'' def index @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } end end # GET /users/1 # GET /users/1.xml def show #@user = User.find(params[:id]) @user = @current_user respond_to do |format| format.html # show.html.erb format.xml { render :xml => @user } end end # GET /users/new # GET /users/new.xml def new @user = User.new @user_session = UserSession.new @user.addresses.build respond_to do |format| format.html # new.html.erb format.xml { render :xml => @user } end end # GET /users/1/edit def edit # @user = User.find(params[:id]) @user = @current_user end # POST /users # POST /users.xml def create @user = User.new(params[:user]) respond_to do |format| if @user.save flash[:notice] = ''Registration successful.'' format.html { redirect_to(@user) } format.xml { render :xml => @user, :status => :created, :location => @user } else format.html { render :action => "new" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end # PUT /users/1 # PUT /users/1.xml def update # @user = User.find(params[:id]) @user = @current_user respond_to do |format| if @user.update_attributes(params[:user]) flash[:notice] = ''Account was successfully updated.'' format.html { redirect_to(@user) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @user.errors, :status => :unprocessable_entity } end end end # DELETE /users/1 # DELETE /users/1.xml def destroy @user = User.find(params[:user]) @user.destroy respond_to do |format| format.html { redirect_to(users_url) } format.xml { head :ok } end end end ************************************************ user.rb model: ************************************************ class User < ActiveRecord::Base acts_as_authentic has_many :addresses accepts_nested_attributes_for :addresses, :allow_destroy => true def address_attributes=(address_attributes) address_attributes.each do |attributes| addresses.build(attributes) end end How do I make the default registration address be the default shipping_address? I am using address here but should it be shipping_address? Should I use a hidden field to set the type and modify the create section of the code? ************************************************ addresses_controller.rb ************************************************ class AddressesController < ApplicationController # GET /addresses # GET /addresses.xml def index @addresses = Address.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @addresses } end end # GET /addresses/1 # GET /addresses/1.xml def show @address = Address.find(params[:id]) respond_to do |format| format.html # show.html.erb format.xml { render :xml => @address } end end # GET /addresses/new # GET /addresses/new.xml def new @address = Address.new respond_to do |format| format.html # new.html.erb format.xml { render :xml => @address } end end # GET /addresses/1/edit def edit @address = Address.find(params[:id]) end # POST /addresses # POST /addresses.xml def create @address = Address.new(params[:address]) respond_to do |format| if @address.save flash[:notice] = ''Address was successfully created.'' format.html { redirect_to(@address) } format.xml { render :xml => @address, :status => :created, :location => @address } else format.html { render :action => "new" } format.xml { render :xml => @address.errors, :status => :unprocessable_entity } end end end # PUT /addresses/1 # PUT /addresses/1.xml def update @address = Address.find(params[:id]) respond_to do |format| if @address.update_attributes(params[:address]) flash[:notice] = ''Address was successfully updated.'' format.html { redirect_to(@address) } format.xml { head :ok } else format.html { render :action => "edit" } format.xml { render :xml => @address.errors, :status => :unprocessable_entity } end end end # DELETE /addresses/1 # DELETE /addresses/1.xml def destroy @address = Address.find(params[:id]) @address.destroy respond_to do |format| format.html { redirect_to(addresses_url) } format.xml { head :ok } end end end ************************************************ address.rb model ************************************************ class Address < ActiveRecord::Base belongs_to :user end ************************************************ billing_address.rb ************************************************ class BillingAddress < Address end ************************************************ shipping_address.rb ************************************************ class ShippingAddress < Address end ************************************************ -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=.
Rick DeNatale
2009-Nov-22 00:27 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
On Sat, Nov 21, 2009 at 4:09 AM, bui <bui411-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Hi - Very new to RoR. I have looked around at different strategies > for handling shipping and billing addresses for an e-commerce > applications. What I am trying to do is to have a user registration > form where the user need to also fill out the default shipping > address. Ultimately, I would like for the users to be able to add > additional shipping address and billing addresses. > > I am sort of going down the path outlined by ryanb''s here and using > Single Table Inheritance where the shipping address and billing > address inherits from address class. Is this the best approach? > > ryanb suggested something along the lines below: > > Customer > has_many :addresses > has_many :orders > > Address > belongs_to :customer > > Order > belongs_to :customer > belongs_to :billing_address #... > belongs_to :shipping_address #... > > Is this an instance where I need to use both STI and polymorphic > association?No I don''t think you need either. You have a case where an Order can have two different addresses, but they are both always Addresses.> > I haven''t created the order.rb model yet, but ryanb did suggest making > sure that... > > "The billing_address_id column would go in the orders table. Same > with the shipping." > > Is this the only table in the database that would contain this > column? Should they also exist in the address table or is this > handled by Rails through the type column?I don''t think that they need to be in the address table, in this case. Let''s see.> Here''s my current code but I''m certain I''m missing some key concepts > here. I am providing some of the code below. Any help would be > greatly appreciated. > > users_controller.rb > ************************************************ > class UsersController < ApplicationController> user.rb model: > ************************************************ > class User < ActiveRecord::Base > acts_as_authentic > has_many :addresses > accepts_nested_attributes_for :addresses, :allow_destroy => true > > def address_attributes=(address_attributes) > address_attributes.each do |attributes| > addresses.build(attributes) > end > end > > How do I make the default registration address be the default > shipping_address? I am using address here but should it be > shipping_address? Should I use a hidden field to set the type and > modify the create section of the code?This isn''t really an issue with the user model. User''s don''t have a shipping address, they just have one or more addresses. Now they might have one of those addresses be the one they normal want orders shipped to. I''ll get to setting the defaults for a order at the end.> ************************************************ > > addresses_controller.rb > ************************************************ > class AddressesController < ApplicationController > # GET /addresses > # GET /addresses.xml > > def index > @addresses = Address.all > > respond_to do |format| > format.html # index.html.erb > format.xml { render :xml => @addresses } > end > end > > # GET /addresses/1 > # GET /addresses/1.xml > def show > @address = Address.find(params[:id]) > > respond_to do |format| > format.html # show.html.erb > format.xml { render :xml => @address } > end > end > > # GET /addresses/new > # GET /addresses/new.xml > def new > @address = Address.new > > respond_to do |format| > format.html # new.html.erb > format.xml { render :xml => @address } > end > end > > # GET /addresses/1/edit > def edit > @address = Address.find(params[:id]) > end > > # POST /addresses > # POST /addresses.xml > def create > @address = Address.new(params[:address]) > > respond_to do |format| > if @address.save > flash[:notice] = ''Address was successfully created.'' > format.html { redirect_to(@address) } > format.xml { render :xml => @address, :status > => :created, :location => @address } > else > format.html { render :action => "new" } > format.xml { render :xml => @address.errors, :status > => :unprocessable_entity } > end > end > end > > # PUT /addresses/1 > # PUT /addresses/1.xml > def update > @address = Address.find(params[:id]) > > respond_to do |format| > if @address.update_attributes(params[:address]) > flash[:notice] = ''Address was successfully updated.'' > format.html { redirect_to(@address) } > format.xml { head :ok } > else > format.html { render :action => "edit" } > format.xml { render :xml => @address.errors, :status > => :unprocessable_entity } > end > end > end > > # DELETE /addresses/1 > # DELETE /addresses/1.xml > def destroy > @address = Address.find(params[:id]) > -Jbz+COfnBigqAwPp7cWvug@public.gmane.org > > respond_to do |format| > format.html { redirect_to(addresses_url) } > format.xml { head :ok } > end > end > end > ************************************************The main comment I''ve got about this, is that if every address belongs to a user, then the address controller should be scoped to a user, and instead of Address.find, Address.new, Address.create you should find instantiate and create addresses using the addresses association of user.> > address.rb model > ************************************************ > class Address < ActiveRecord::Base > belongs_to :user > end > ************************************************ > > billing_address.rb > ************************************************ > class BillingAddress < Address > end > ************************************************ > > shipping_address.rb > ************************************************ > class ShippingAddress < Address > end > ************************************************You don''t need subclasses unless there is a behavioral difference between billing and shipping addresses. I doubt that there is, they are both addresses, it''s just that they are playing different roles. If you change the Order model slightly Order belongs_to :customer belongs_to :billing_address, :class_name => ''Address'' belongs_to :shipping_address, :class_name => ''Address'' Then you can use the Address class for both roles. Now having gotten here, you could also model the notion of a customer having a preferred shipping address (and even a preferred billing address) in a similar way User ... belongs_to :preferred_billing_address, :class_name => "Address" belongs_to :preferred_shipping_address, :class_name => "Address" It might seem odd to have a User ''belonging to'' two particular addresses, but you can alternatively thing of belongs_to as has_a_pointer_to, and has_many as has_many_pointing_to_me. Now about the order. I think that there are at least two ways of dealing with the billing and shipping addresses: 1) in the OrdersController def new @order = current_user.orders.build( :billing_address => current_user.preferred_billing_address, :shipping_address => current_user.preferred_shipping_address ) #... end 2) in the OrderModel class Order < ActiveRecord::Base belongs_to :customer belongs_to :billing_address, :class_name => ''Address'' belongs_to :shipping_address, :class_name => ''Address'' def after_initialize if new_record? self.billing_address customer.preferred_billing_address unless billing_address self.shipping_address customer.preferred_shipping_address unless shipping_address end end end The first approach is probably more conventional. HTH -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=.
Philip Hallstrom
2009-Nov-22 01:12 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
> > Order > belongs_to :customer > belongs_to :billing_address, :class_name => ''Address'' > belongs_to :shipping_address, :class_name => ''Address'' > ....snip.... > 1) in the OrdersController > > def new > @order = current_user.orders.build( > :billing_address => > current_user.preferred_billing_address, > :shipping_address => > current_user.preferred_shipping_address > ) > #... > endOne thing that popped into my head while reading this is that given the above, if I update my shipping/billing address, any orders I''ve previously placed will have their addresses updated as well. Which may not be what you want -- since you''d lose the ability to tell where an order was shipped should I do that. So, you might want to add some logic to the Address model that if the address currently being changed is associated with any orders that instead a new address should be created and the old one set to some sort of "archived, but need to keep around as it relates to an order" status. This is similar to needing to store the price of the product in the order itself so that if you later change the price you aren''t retroactively changing the order information... -philip -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=.
Rick DeNatale
2009-Nov-22 05:27 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
On Sat, Nov 21, 2009 at 8:12 PM, Philip Hallstrom <philip-LSG90OXdqQE@public.gmane.org> wrote:>> >> Order >> belongs_to :customer >> belongs_to :billing_address, :class_name => ''Address'' >> belongs_to :shipping_address, :class_name => ''Address'' >> ....snip.... >> 1) in the OrdersController >> >> def new >> @order = current_user.orders.build( >> :billing_address => >> current_user.preferred_billing_address, >> :shipping_address => >> current_user.preferred_shipping_address >> ) >> #... >> end > > One thing that popped into my head while reading this is that given > the above, if I update my shipping/billing address, any orders I''ve > previously placed will have their addresses updated as well.No they won''t. When the order is saved, then it will have a shipping_address_id and billing_address_id set to either the values intialized by the controller in the new method (which is used as a template for the values posted back to the create method by the form) or whatever the user changed it to on the order form. If the user changes his preferences later, it won''t affect existing orders. I think you might be better off just to write some code and test it and see what happens, rather than continuing with what seem to be gedanken experiments. -- Rick DeNatale Blog: http://talklikeaduck.denhaven2.com/ Twitter: http://twitter.com/RickDeNatale WWR: http://www.workingwithrails.com/person/9021-rick-denatale LinkedIn: http://www.linkedin.com/in/rickdenatale -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=.
Philip Hallstrom
2009-Nov-22 16:50 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
> On Sat, Nov 21, 2009 at 8:12 PM, Philip Hallstrom <philip-LSG90OXdqQE@public.gmane.org> > wrote: >>> >>> Order >>> belongs_to :customer >>> belongs_to :billing_address, :class_name => ''Address'' >>> belongs_to :shipping_address, :class_name => ''Address'' >>> ....snip.... >>> 1) in the OrdersController >>> >>> def new >>> @order = current_user.orders.build( >>> :billing_address => >>> current_user.preferred_billing_address, >>> :shipping_address => >>> current_user.preferred_shipping_address >>> ) >>> #... >>> end >> >> One thing that popped into my head while reading this is that given >> the above, if I update my shipping/billing address, any orders I''ve >> previously placed will have their addresses updated as well. > > No they won''t. > > When the order is saved, then it will have a shipping_address_id and > billing_address_id set to either the values intialized by the > controller in the new method (which is used as a template for the > values posted back to the create method by the form) or whatever the > user changed it to on the order form. > > If the user changes his preferences later, it won''t affect existing > orders. > > I think you might be better off just to write some code and test it > and see what happens, rather than continuing with what seem to be > gedanken experiments.I think you misunderstood or I didn''t make it clear... if the user changes his preferred address, everything will be fine. If there is functionality that lets a user update his old addresses then there will be a problem in that the order will be updated too. Say the user ships things to his work address. Then he changes jobs and updates his work address... any order that had a shipping address of that same id will now have the new address, not the old. class Address < ActiveRecord::Base end class Customer < ActiveRecord::Base belongs_to :preferred_billing_address, :class_name => "Address" belongs_to :preferred_shipping_address, :class_name => "Address" has_many :orders end class Order < ActiveRecord::Base belongs_to :customer belongs_to :billing_address, :class_name => ''Address'' belongs_to :shipping_address, :class_name => ''Address'' end create_table "addresses", :force => true do |t| t.string "street" t.datetime "created_at" t.datetime "updated_at" end create_table "customers", :force => true do |t| t.string "name" t.integer "preferred_billing_address_id" t.integer "preferred_shipping_address_id" t.datetime "created_at" t.datetime "updated_at" end create_table "orders", :force => true do |t| t.integer "customer_id" t.integer "billing_address_id" t.integer "shipping_address_id" t.datetime "created_at" t.datetime "updated_at" end >> c = Customer.create(:name => ''Philip'') >> a1 = Address.create(:street => ''123 Maple St'') >> a2 = Address.create(:street => ''456 Syrup St'') >> c.preferred_shipping_address = a1 >> c.preferred_billing_address = a2 >> c.save >> o = c.orders.build(:shipping_address => c.preferred_shipping_address, :billing_address => c.preferred_billing_address) >> c.preferred_shipping_address.street => "123 Maple St" >> c.preferred_billing_address.street => "456 Syrup St" >> o.shipping_address.street => "123 Maple St" >> o.billing_address.street => "456 Syrup St" >> a1.street = ''789 Blueberry St'' => "789 Blueberry St" >> a1.save >> c.preferred_shipping_address.street => "789 Blueberry St" >> o.shipping_address.street => "789 Blueberry St" -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=.
bui
2009-Nov-25 06:34 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
Rick, thanks for responding. I appreciate the help. I wasn''t familiar with scoping but after re On Nov 21, 4:27 pm, Rick DeNatale <rick.denat...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Sat, Nov 21, 2009 at 4:09 AM, bui <bui...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > Hi - Very new to RoR. I have looked around at different strategies > > for handling shipping and billing addresses for an e-commerce > > applications. What I am trying to do is to have a user registration > > form where the user need to also fill out the default shipping > > address. Ultimately, I would like for the users to be able to add > > additional shipping address and billing addresses. > > > I am sort of going down the path outlined by ryanb''s here and using > > Single Table Inheritance where the shipping address and billing > > address inherits from address class. Is this the best approach? > > > ryanb suggested something along the lines below: > > > Customer > > has_many :addresses > > has_many :orders > > > Address > > belongs_to :customer > > > Order > > belongs_to :customer > > belongs_to :billing_address #... > > belongs_to :shipping_address #... > > > Is this an instance where I need to use both STI and polymorphic > > association? > > No I don''t think you need either. You have a case where an Order can > have two different addresses, but they are both always Addresses. >How do I distinguish between when an address is a shipping and when it is a billing address? How does it get stored in the database?> > > I haven''t created the order.rb model yet, but ryanb did suggest making > > sure that... > > > "The billing_address_id column would go in the orders table. Same > > with the shipping." > > > Is this the only table in the database that would contain this > > column? Should they also exist in the address table or is this > > handled by Rails through the type column? > > I don''t think that they need to be in the address table, in this case. > Let''s see. >So, I''m assuming the type column will be used.> > > Here''s my current code but I''m certain I''m missing some key concepts > > here. I am providing some of the code below. Any help would be > > greatly appreciated. > > > users_controller.rb > > ************************************************ > > class UsersController < ApplicationController > > user.rb model: > > ************************************************ > > class User < ActiveRecord::Base > > acts_as_authentic > > has_many :addresses > > accepts_nested_attributes_for :addresses, :allow_destroy => true > > > def address_attributes=(address_attributes) > > address_attributes.each do |attributes| > > addresses.build(attributes) > > end > > end > > > How do I make the default registration address be the default > > shipping_address? I am using address here but should it be > > shipping_address? Should I use a hidden field to set the type and > > modify the create section of the code? > > This isn''t really an issue with the user model. User''s don''t have a > shipping address, they just have one or more addresses. Now they > might have one of those addresses be the one they normal want orders > shipped to. > > I''ll get to setting the defaults for a order at the end. > > > > > ************************************************ > > > addresses_controller.rb > > ************************************************ > > class AddressesController < ApplicationController > > # GET /addresses > > # GET /addresses.xml > > > def index > > @addresses = Address.all > > > respond_to do |format| > > format.html # index.html.erb > > format.xml { render :xml => @addresses } > > end > > end > > > # GET /addresses/1 > > # GET /addresses/1.xml > > def show > > @address = Address.find(params[:id]) > > > respond_to do |format| > > format.html # show.html.erb > > format.xml { render :xml => @address } > > end > > end > > > # GET /addresses/new > > # GET /addresses/new.xml > > def new > > @address = Address.new > > > respond_to do |format| > > format.html # new.html.erb > > format.xml { render :xml => @address } > > end > > end > > > # GET /addresses/1/edit > > def edit > > @address = Address.find(params[:id]) > > end > > > # POST /addresses > > # POST /addresses.xml > > def create > > @address = Address.new(params[:address]) > > > respond_to do |format| > > if @address.save > > flash[:notice] = ''Address was successfully created.'' > > format.html { redirect_to(@address) } > > format.xml { render :xml => @address, :status > > => :created, :location => @address } > > else > > format.html { render :action => "new" } > > format.xml { render :xml => @address.errors, :status > > => :unprocessable_entity } > > end > > end > > end > > > # PUT /addresses/1 > > # PUT /addresses/1.xml > > def update > > @address = Address.find(params[:id]) > > > respond_to do |format| > > if @address.update_attributes(params[:address]) > > flash[:notice] = ''Address was successfully updated.'' > > format.html { redirect_to(@address) } > > format.xml { head :ok } > > else > > format.html { render :action => "edit" } > > format.xml { render :xml => @address.errors, :status > > => :unprocessable_entity } > > end > > end > > end > > > # DELETE /addresses/1 > > # DELETE /addresses/1.xml > > def destroy > > @address = Address.find(params[:id]) > > -Jbz+COfnBigqAwPp7cWvug@public.gmane.org > > > respond_to do |format| > > format.html { redirect_to(addresses_url) } > > format.xml { head :ok } > > end > > end > > end > > ************************************************ > > The main comment I''ve got about this, is that if every address belongs > to a user, then the address controller should be scoped to a user, and > instead of Address.find, Address.new, Address.create you should find > instantiate and create addresses using the addresses association of > user. >Yes, you are correct. I was not familiar with the scoping aspect of rails and after some reading it appears that it is probably good practice since without it any user can change another users address. That''s sort of what I gathered from reading about it.> > > > > address.rb model > > ************************************************ > > class Address < ActiveRecord::Base > > belongs_to :user > > end > > ************************************************ > > > billing_address.rb > > ************************************************ > > class BillingAddress < Address > > end > > ************************************************ > > > shipping_address.rb > > ************************************************ > > class ShippingAddress < Address > > end > > ************************************************ > > You don''t need subclasses unless there is a behavioral difference > between billing and shipping addresses. I doubt that there is, they > are both addresses, it''s just that they are playing different roles. > > If you change the Order model slightly > > Order > belongs_to :customer > belongs_to :billing_address, :class_name => ''Address'' > belongs_to :shipping_address, :class_name => ''Address'' > > Then you can use the Address class for both roles. >Again, I understand what you are saying here, but where (i.e. - in the address table and using the type column or in their own tables) would the shipping and billing addresses be stored or designated in the database?> Now having gotten here, you could also model the notion of a customer > having a preferred shipping address (and even a preferred billing > address) in a similar way > > User > ... > belongs_to :preferred_billing_address, :class_name => "Address" > belongs_to :preferred_shipping_address, :class_name => "Address" > > It might seem odd to have a User ''belonging to'' two particular > addresses, but you can alternatively thing of belongs_to as > has_a_pointer_to, and has_many as has_many_pointing_to_me. > > Now about the order. I think that there are at least two ways of > dealing with the billing and shipping addresses: > > 1) in the OrdersController > > def new > @order = current_user.orders.build( > :billing_address => > current_user.preferred_billing_address, > :shipping_address => > current_user.preferred_shipping_address > ) > #... > end > > 2) in the OrderModel > > class Order < ActiveRecord::Base > belongs_to :customer > belongs_to :billing_address, :class_name => ''Address'' > belongs_to :shipping_address, :class_name => ''Address'' > > def after_initialize > if new_record? > self.billing_address > customer.preferred_billing_address unless billing_address > self.shipping_address > customer.preferred_shipping_address unless shipping_address > end > end > end > > The first approach is probably more conventional. > > HTH > > -- > Rick DeNatale > > Blog:http://talklikeaduck.denhaven2.com/ > Twitter:http://twitter.com/RickDeNatale > WWR:http://www.workingwithrails.com/person/9021-rick-denatale > LinkedIn:http://www.linkedin.com/in/rickdenatale-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.
bui
2009-Nov-25 06:43 UTC
Re: Strategy for Handling Separate Shipping and Billing Addresses
Hi Philip - Thanks for responding. Yeah, I thought about what happens when they go to change an old address. Not sure how to handle it at this point. I did find a plugin written by Ryan Bates that at least handled "freezing" an association. http://github.com/ryanb/association-freezer/blob/4691b3247332ac9a5d534206e80080a54ff0a48d/association-freezer.gemspec Seems like an interesting piece of code and I''ll have to learn more about it. I see that you ran a test but I noticed that you used:> class Customer < ActiveRecord::Base > belongs_to :preferred_billing_address, :class_name => "Address" > belongs_to :preferred_shipping_address, :class_name => "Address" > has_many :orders > endWhat happens if you use has_many instead of belongs_to? It just makes more sense to me that a Customer can have many addresses regardless of whether they are of type shipping or billing. On Nov 22, 8:50 am, Philip Hallstrom <phi...-LSG90OXdqQE@public.gmane.org> wrote:> > On Sat, Nov 21, 2009 at 8:12 PM, Philip Hallstrom <phi...-LSG90OXdqQE@public.gmane.org> > > wrote: > > >>> Order > >>> belongs_to :customer > >>> belongs_to :billing_address, :class_name => ''Address'' > >>> belongs_to :shipping_address, :class_name => ''Address'' > >>> ....snip.... > >>> 1) in the OrdersController > > >>> def new > >>> @order = current_user.orders.build( > >>> :billing_address => > >>> current_user.preferred_billing_address, > >>> :shipping_address => > >>> current_user.preferred_shipping_address > >>> ) > >>> #... > >>> end > > >> One thing that popped into my head while reading this is that given > >> the above, if I update my shipping/billing address, any orders I''ve > >> previously placed will have their addresses updated as well. > > > No they won''t. > > > When the order is saved, then it will have a shipping_address_id and > > billing_address_id set to either the values intialized by the > > controller in the new method (which is used as a template for the > > values posted back to the create method by the form) or whatever the > > user changed it to on the order form. > > > If the user changes his preferences later, it won''t affect existing > > orders. > > > I think you might be better off just to write some code and test it > > and see what happens, rather than continuing with what seem to be > > gedanken experiments. > > I think you misunderstood or I didn''t make it clear... if the user > changes his preferred address, everything will be fine. If there is > functionality that lets a user update his old addresses then there > will be a problem in that the order will be updated too. Say the user > ships things to his work address. Then he changes jobs and updates > his work address... any order that had a shipping address of that same > id will now have the new address, not the old. > > class Address < ActiveRecord::Base > end > class Customer < ActiveRecord::Base > belongs_to :preferred_billing_address, :class_name => "Address" > belongs_to :preferred_shipping_address, :class_name => "Address" > has_many :orders > end > class Order < ActiveRecord::Base > belongs_to :customer > belongs_to :billing_address, :class_name => ''Address'' > belongs_to :shipping_address, :class_name => ''Address'' > end > > create_table "addresses", :force => true do |t| > t.string "street" > t.datetime "created_at" > t.datetime "updated_at" > end > > create_table "customers", :force => true do |t| > t.string "name" > t.integer "preferred_billing_address_id" > t.integer "preferred_shipping_address_id" > t.datetime "created_at" > t.datetime "updated_at" > end > > create_table "orders", :force => true do |t| > t.integer "customer_id" > t.integer "billing_address_id" > t.integer "shipping_address_id" > t.datetime "created_at" > t.datetime "updated_at" > end > > >> c = Customer.create(:name => ''Philip'') > >> a1 = Address.create(:street => ''123 Maple St'') > >> a2 = Address.create(:street => ''456 Syrup St'') > >> c.preferred_shipping_address = a1 > >> c.preferred_billing_address = a2 > >> c.save > >> o = c.orders.build(:shipping_address => > c.preferred_shipping_address, > :billing_address => c.preferred_billing_address) > >> c.preferred_shipping_address.street > => "123 Maple St" > >> c.preferred_billing_address.street > => "456 Syrup St" > >> o.shipping_address.street > => "123 Maple St" > >> o.billing_address.street > => "456 Syrup St" > >> a1.street = ''789 Blueberry St'' > => "789 Blueberry St" > >> a1.save > >> c.preferred_shipping_address.street > => "789 Blueberry St" > >> o.shipping_address.street > => "789 Blueberry St"-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.