0

I've got two nested resources I'm working with here. Stashes & Receivers.

routes.rb

resources :stashes do
  resources :receivers
end

I have a form where I'd like to create both a Stash and a Receiver once it's submitted.

<%= form_for(@stash) do |f| %>
  <h2>Who is this Stash for?</h2>
  <div class="reciever-wrap">
    <%= f.fields_for @receiver do |builder| %>
      <div><%= builder.label :first_name %><br />
      <%= builder.text_field :first_name, :autofocus => true %></div>

      <div><%= builder.label :last_name %><br />
      <%= builder.text_field :last_name %></div>

    <% end %>
   <div class="self-check">
      <%= check_box_tag(:self) %>
      <%= label_tag(:self, "This stash is for me") %></div>
  </div>

  <div><%= f.label :occasion, "Is this stash for an occassion?" %><br />
  <%= f.text_field :occasion %></div>

  <div><%= f.label :initial_amount, "How much would you like to spend?" %><br />
  <%= f.text_field :initial_amount %></div>

  <div><%= f.label :length, "How long would you like this Stash to last?" %><br />
  <%= select_tag(:length, options_for_select([['3 Months', 1],['6 Months', 2],['1 Year']])) %>    </div>

  <div><%= f.submit "Start adding gifts to this Stash!", :class => "button" %></div>
<% end %>

I think that it makes the most sense to create the Receiver first, and then the Stash, so I can associate the Stash with the Receiver. So in the Stashes controller (because the Stash is primary in this form) I have this in the create action:

stashes_controller.rb

def create
  receiver = Receiver.create_receiver(current_user, stash_params)
  if receiver.id
    @stash = Stash.create_stash(current_user, stash_params, receiver)
    if @stash.id
      flash[:success] = "Stash Started!"
      redirect_to stashes_url(@stash), :notice => "Stash Started!"
    else
      render :action => "new", :notice => "Error, try that again"
    end
  end
end

I'm also defining the stash_params at the bottom:

def stash_params
  params.require(:stash).permit(
    :intitial_amount,
    :length,
    :user_id,
    :first_name,
    :last_name,
    :id,
    :receivers_attributes => [:id, :first_name, :last_name])
end

I'm able to create the Stash no problem. But when I go to create the Receiver, I'm running into trouble. The system is creating a new Receiver record, but none of the data from the form is being stored. I'm guessing that it has something to do with not being able to access that nested strong parameter data.

This is the create_receiver method in the Receiver model that should be saving the data, but for some reason isn't. I am able to save the current_user data, so that's good. It's only the data from the strong attributes that isn't being saved:

receiver.rb

def self.create_receiver(current_user, stash_params)
  Receiver.create!(
    :first_name => stash_params[:first_name],
    :last_name => stash_params[:last_name],
    :user_id => current_user
  )
end

Do I need to be traversing the stash_params to access the nested receiver_params attributes? I'm not sure how I should be accessing the nested attributes in the model. Any help is greatly appreciated!

I also tried moving this create method into the Stash model to see if I could access the attributes from there, but I had no luck with that either.

Dave Dawson
  • 77
  • 1
  • 2
  • 11

2 Answers2

1

You're probably missing accepts_nested_attributes_for in your model

http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

Update #1

Since you've stated that you've included accepts_nested_attributes_for I would suggest consolidating your controller logic to something like

def create
  @stash = Stash.new(stash_params)
  @stash.user = current_user
  @stash.receivers.each { |r| r.user = current_user }

  if @stash.save
      flash[:success] = "Stash Started!"
      redirect_to stashes_url(@stash), :notice => "Stash Started!"
    else
      render :action => "new", :notice => "Error, try that again"
    end
  end
end

This way you don't have to check if the individual records were being saved and strong parameters should act properly

Kyle Decot
  • 20,715
  • 39
  • 142
  • 263
  • Thanks, Kyle. I do have `accepts_nested_attributes_for` in both the Receiver and Stash models. That was the first thing I checked! – Dave Dawson Feb 20 '14 at 04:37
  • I'm getting an error on the `@stash.receivers.each` method on here. It's because the receiver hasn't been created yet, I believe. This issue I'm having is accessing the receivers_params in the Receiver model. – Dave Dawson Feb 20 '14 at 16:03
1

Since you're using nested attributes, you should try this in your create action:

def create
  stash = Stash.new(stash_params)
  if stash.save
    flash[:success] = "Stash Started!"
    redirect_to stashes_url(@stash), :notice => "Stash Started!"
  else
    render :action => "new", :notice => "Error, try that again"
  end
end

This does all the work needed to save both the stash and receiver. accepts_nested_attribute_for guarantees that the belonged object will be saved and receive the proper id from the parent object when it's being saved while the parent object is also being stored.

Allam Matsubara
  • 517
  • 5
  • 18