0

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?

San Diago
  • 1,030
  • 1
  • 12
  • 26
  • 1
    Since comments are nested under sections in your routes, you should find the section in params[:section_id] instead of params[: id] in your create action. – hashrocket May 25 '19 at 03:05

2 Answers2

2

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
Mark Merritt
  • 2,625
  • 2
  • 12
  • 16
  • I ended up making some changes and had gotten it to work somehow, so I'm not 100% sure what did it, but I tried it now with `@section.create_comment(comment_params)` and it still works, so I'm accepting the answer. Thanks! – San Diago May 25 '19 at 13:56
0

You likely need to change:

@section.comment.create(comment_params)

to:

@section.comments.create(comment_params)

If that doesn't work, try:

@section.comment.create!(comment_params)

and see what the exception says

NM Pennypacker
  • 6,704
  • 11
  • 36
  • 38