Search code examples
ruby-on-railsupdate-attributeswicked-gem

Rails update_attributes causing object to be duplicated every time it hits update controller action


I'm using Rails and the Wicked Gem to create a multi-step form. I have a Parent and a Child model. Parents have many children, and the parents form accepts_nested_attributes_for :children.

I'm building a nested object on the SHOW action of the controller so that a form field displays.

For some reason, every time the form is saved the number of children in the database (and the number of form fields on the view) are doubled. First it will save 1 child as expected. Then if I go back to update that part of the form and save that child, it creates 2 children, then 4, etc.

Here is the relevant code:

parent.rb

class Parent < ApplicationRecord
    belongs_to :user
    has_many :children
    accepts_nested_attributes_for :children
end

child.rb

class Child < ApplicationRecord
    belongs_to :parent
end

parent_steps_controller.rb

class ParentStepsController < ApplicationController
    include Wicked::Wizard
    steps :spouse, :children

    def show
        @user = current_user
        if Parent.find_by(id: params[:parent_id])
            @parent = Parent.find(params[:parent_id])
            session[:current_parent_id] = @parent.id
        else
            @parent = Parent.find_by(id: session[:current_parent_id])
        end
        @parent.children.build
        render_wizard
    end

    def update
        @parent = Parent.find_by(id: session[:current_parent_id])
        @parent.update_attributes(parent_params)
        render_wizard @parent
    end


    private

    def parent_params
        params.require(:parent).permit(:spouse_name, :children_attributes => [:full_name])
    end

end

children.html.erb

<%= form_for(@parent, :url=> wizard_path, :method => :put) do |f| %>
<h1>Children Information</h1>
    <%= f.fields_for :children do |child| %>
        <div class="field">
            <%= child.label :full_name %>
            <%= child.text_field :full_name %>
        </div>
    <% end %>

<%= f.submit "Continue", :class => "btn btn-primary" %>
<% end %>

Solution

  • You need to add the child.id to the set returned. If rails doesn't see an ID, it assumes these are new records. It doesn't have to be visible to you, but must be part of the form.

    <%= f.fields_for :children do |child| %>
      <%= child.hidden_field :id %>
    

    Also ensure your strong parameters allow :id to be passed through for the children_attributes part.

    def parent_params
        params.require(:parent).permit(:spouse_name, :children_attributes => [:id, :full_name])
    end