Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails - one form to two models

I am building a form in Rails (version 4.2.5) to collect basic information about stores, and I would like to have the form submit data to two separate models - one called Store, which collects information about the store, and one called Address, which just holds the address info. I've seen this question floating around Stack and elsewhere, but I'm pretty new to Rails and could use a little more step-by-step guidance. (Also, I'm aware of the Rails geocoder gem, but don't want to use it in this project despite the fact that I am collecting address and lat/long info)

For starters, here are my Store and Address models...

class Store < ActiveRecord::Base
  belongs_to :user
  belongs_to :store_type
  has_one :address
end

class Address < ActiveRecord::Base
  belongs_to :store
end

and my StoresController...

class StoresController < ApplicationController

    def new
        @store = Store.new
        @storeType = StoreType.all
    end

    def create
        @store = Store.new(store_params)
        @store.user = current_user

        if @store.save
            flash[:notice] = "New store created"
            redirect_to root_path
        else
            #ERROR STUFF
        end
    end

    private
    def store_params
        params.require(:store).permit(:user_id, :store_type_id, :latitude, :longitude, :name, :notes)
    end
end

Finally, the form for creating a new store (not including address)...

<%= form_for @store do |f| %>

    <%= f.hidden_field :latitude, :id => "latitude_field"  %>
    <%= f.hidden_field :longitude, :id => "longitude_field"  %>

    <div class="field">
        <%= f.label :store_type_id %>
        <%= f.select :store_type_id, @storeType.map{ |type| [type.store_type.capitalize, type.id] } %>
    </div>

    <div class="field">
        <%= f.label :name %>
        <%= f.text_field :name  %>
    </div>

    <div class="field">
        <%= f.label :notes %>
        <%= f.text_field :notes  %>
    </div>

    <div class="actions">
        <%= f.submit :"Submit" %>
    </div>
<% end %>

So the finished form will pass the above fields to the Store model, and should also include fields for street, city, state, and zip, which are the fields that will be passed to the Address model.

Overall, what is the best approach for this?

And, as a follow-up question, my Address model has a column for store_id - will this column populate automatically with the id for the store as it is being created if both Store and Address models are being populated from the same form? Or am I getting way out of my league here?

Thank you.

like image 542
skwidbreth Avatar asked Jan 18 '16 20:01

skwidbreth


2 Answers

You'll be best looking into accepts_nested_attributes_for:

#app/models/store.rb
class Store < ActiveRecord::Base
   has_one :address
   accepts_nested_attributes_for :address
end

#app/controllers/stores_controller.rb
class StoresController < ApplicationController
   def new
      @store = current_user.stores.new
      @store.build_address
   end

   def create
      @store = current_user.stores.new store_params
      @store.save
   end

   private

   def store_params
      params.require(:store).permit(:store_type_id, :latitude, :longitude, :name, :notes, address_attributes: [:line_1])
   end
end

#app/views/stores/new.html.erb
<%= form_for @store do |f| %>
   <%= f.fields_for :address do |a| %>
      <%= a.text_field :line_1 %>
   <% end %>
   <%= f.submit %>
<% end %>
like image 57
Richard Peck Avatar answered Nov 15 '22 00:11

Richard Peck


You can use accept_nested_attributes_for :address in the Store model, and then instantiate the addres form with fields_for

Something like this:

class Store < ActiveRecord::Base
  has_one :address
  accept_nested_attributes_for :address
end

class StoresController < ApplicationController

  def new
    @store = Store.new
    @store.build_address
  end
end

<%= form_for @store do |f| %>
  <%= f.fields_for @store.address do |ff| %>

You also need to whitelist address_attributes in the StoreController and save the address once the Store object is saved.

like image 41
Marcelo Risoli Avatar answered Nov 14 '22 23:11

Marcelo Risoli