ruby-on-railsformsruby-on-rails-5nested-resources

Having trouble submitting nested resource through form [Rails 5]


I have a Document that has_many Section, and each section has_one Comment. I want to be able to create both sections and comments in the Document show view, but I'm having trouble getting comments to go through.

Here's the relevant code with the closest I've got:

class CommentsController < ApplicationController
  def create
    @section = Section.find(params[:id])
    @section.comment.create(comment_params)
  end

  private

    def comment_params
      params.require(:comment).permit(:body)
    end
end

The routing:

resources :documents, shallow: true do
  resources :sections do
    resources :comments
  end
end 

And the view with the form:

# app/views/documents/show.html.erb

<% @document.sections.each do |section| %>
  <%= section.body %>

  <% if section.comment %>
    <p>
      <%= section.comment %>
    </p>
  <% else %>
    <%= form_with url: section_comments_path(section.id), scope: 'comment' do |form| %>
      <%= form.text_field :body, placeholder: "Comment" %>
      <%= form.submit %>
    <% end %>
  <% end %>
<% end %>

It all seems to check out for me, but when I try to post a comment, here's what I get:

Started POST "/sections/51/comments" for ::1 at 2019-05-24 23:29:06 +0000
Processing by CommentsController#create as JS
  Parameters: {"utf8"=>"✓", "authenticity_token"=>[...], "comment"=>{"body"=>"asdas"}, "commit"=>"Save comment", "section_id"=>"51"}
  Section Load (0.5ms)  SELECT  "sections".* FROM "sections" WHERE "sections"."id" = ? LIMIT ?  [["id", 51], ["LIMIT", 1]]
  comment Load (0.4ms)  SELECT  "comments".* FROM "comments" WHERE "comments"."section_id" = ? LIMIT ?  [["section_id", 51], ["LIMIT", 1]]
Completed 500 Internal Server Error in 11ms (ActiveRecord: 0.9ms)

NoMethodError (undefined method `create' for nil:NilClass):

app/controllers/comments_controller.rb:4:in `create'

Any ideas?


Solution

  • A has_one relationship returns the object itself. Therefore, @section.comment.create(comment_params) will not work because @section.comment is nil. Instead, try something like...

    def create
      @section = Section.find(params[:section_id])
      @comment = Comment.create(comment_params)
      @section.comment = @comment
    
      ...
    end
    

    Or, as stated in the Rails Guides...

    When initializing a new has_one or belongs_to association you must use the build_ prefix to build the association, rather than the association.build method that would be used for has_many or has_and_belongs_to_many associations. To create one, use the create_ prefix.

    Which would look like this

    def create
      @section = Section.find(params[:section_id])
      @section.create_comment(comment_params)
    
      ...
    end