myhouse
myhouse

Reputation: 1991

Rails 5 not saving to my table using nested forms in Ruby-on-Rails using Cocoon gem

RAILS 5 PROBLEM. I have a project and I wanted the user to have the option to add extra items to the form. My code renders correctly and it the link to add more fields does add more text field. The problem I have is that is not saving the to my database. I am using mysql2 gem on a MySQl database. I posted the console output. This is what I have:

forms_controller

  # forms_controller.rb
  def new
    @form = Form.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @form }
    end
  end
  def create
    @form = Form.new(form_params)

    respond_to do |format|
      if @form.save
        format.html { redirect_to(@form, :notice => 'form was successfully created.') }
        format.xml  { render :xml => @form, :status => :created, :location => @form }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @form.errors, :status => :unprocessable_entity }
      end
    end
  end

  def form_params
    params.require(:form).permit(:name, items_attributes: [:id, :item_name, :_destroy])
  end

models

# form.rb
class Form < ApplicationRecord
  has_many :items
  accepts_nested_attributes_for :items, reject_if: :all_blank, allow_destroy: true
  validates_presence_of :name
end

# item.rb
class Item < ApplicationRecord
   belongs_to :form
end

views

<!-- _form.html.haml -->

= form_for @form do |f|
  .field
    = f.label :name
    %br
    = f.text_field :name
  %h3 items
  #items
    = f.fields_for :items do |item|
      = render 'item_fields', f: item
    .links
      = link_to_add_association 'add item', f, :items
  = f.submit
.nested-fields
  .field
    = f.label :item_name
    %br
    = f.text_field :item_name
  = link_to_remove_association "remove item", f

console

tarted POST "/forms" for 127.0.0.1 at 2016-09-08 23:04:17 -0600
Processing by FormsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"FmYPwlXy93iwBMkWvfyd3QJ+NKDcYnraUaKmMISbUIX3+KRL7KtpD33FW3CK+jxvn4AoUUMhx4zrGncJej5BOw==", "form"=>{"name"=>"sfsf", "items_attributes"=>{"1473397456358"=>{"item_name"=>"sfsf", "_destroy"=>"false"}}}, "commit"=>"Create Form"}
   (0.2ms)  BEGIN
   (0.3ms)  ROLLBACK
  Rendering forms/new.html.haml within layouts/application
  Rendered forms/_item_fields.html.haml (1.5ms)
  Rendered forms/_item_fields.html.haml (1.2ms)
  Rendered forms/_form.html.haml (8.5ms)
  Rendered forms/new.html.haml within layouts/application (10.1ms)
Completed 200 OK in 68ms (Views: 54.5ms | ActiveRecord: 0.5ms)

Upvotes: 1

Views: 687

Answers (3)

Bhimasen Rautaray
Bhimasen Rautaray

Reputation: 301

In Rails 5, whenever we define a belongs_to association, it is required to have the associated record present by default after this change.

It triggers validation error if associated record is not present.

class User < ApplicationRecord
end

class Post < ApplicationRecord
  belongs_to :user
end

post = Post.create(title: 'Hi')
=> <Post id: nil, title: "Hi", user_id: nil, created_at: nil, updated_at: nil>

post.errors.full_messages.to_sentence
=> "User must exist"

****Opting out of this default behavior in Rails 5****

We can pass optional: true to the belongs_to association which would remove this validation check.

class Post < ApplicationRecord

  belongs_to :user, optional: true

end

post = Post.create(title: 'Hi')

=> <Post id: 2, title: "Hi", user_id: nil>

Upvotes: 2

myhouse
myhouse

Reputation: 1991

The reason it wouldn't save is because I was using Rails 5 and I needed to add optional: true to the belongs_to in the item model.

# form.rb
class Form < ApplicationRecord
  has_many :items
  accepts_nested_attributes_for :items, reject_if: :all_blank, allow_destroy: true
  validates_presence_of :name
end

# item.rb
class Item < ApplicationRecord
   belongs_to :form , optional: true
end

Upvotes: 1

Taryn East
Taryn East

Reputation: 27747

in your permit/require line (in your controller), item_attribute should probably be item_attributes (there's more than one) ie:

def form_params
  params.require(:form).permit(:name, :date_sent, :quantity, :comment, item_attributes: [:id, :name, :form_id, :_destroy])
end

Upvotes: 2

Related Questions