0

I am trying to re-create a stack overflow like app. One user asks a question, and others can answer. I have a nested form indented on the question's page to get answers from other users.

I am having a difficult time to retrieve the data after the answer is posted, and I have set @answer incorrectly on the questions controller page in the update action, and I can't figure out how to properly retrieve this variable given that the params coming through the questions_controller does not have details of the answer set separately. How do I retrieve the params part related to @answer so that I can set the variable, or maybe I need to use different routes for that? My form looks like this:

 <%= form_for @question do |form| %>
   <%=form.fields_for :answer do |answer_form| %>
     <div class="form-group">
        <%=answer_form.text_area :answer_attributes, placeholder: "Add your answer", rows:10, class:"form-control" %>
<%=answer_form.hidden_field :user_id, value: current_user.id, class:'d-none' %>
         <%=answer_form.hidden_field :question_id, value: @question.id %>
      </div>
    <% end %>

    <div>
       <%=form.submit 'Post Your Answer', class: 'btn-primary' %>
   </div>
 <% end %>

My Question model looks like this:

 class Question < ApplicationRecord
  has_many :answers, dependent: :destroy
  belongs_to :user
  accepts_nested_attributes_for :answers
  validates :headline, presence: true , length: { minimum: 20 }
  validates :body, presence: true, length: { minimum: 50 }
  validates_associated :answers
end

and the Answer model is:

class Answer < ApplicationRecord
  belongs_to :user
  belongs_to :question
  validates :body, presence: true, length: { minimum: 50 }
end

Questions controller:

class QuestionsController < ApplicationController
  before_action :authenticate_user!, except: [:index, :show]
  before_action :set_question, except: [:index, :new, :create]

  def index
    @questions = Question.all.order("id DESC")
  end

  def show
    @question = Question.find(params[:id])
    @user = User.find(@question.user_id)
    @answers = @question.answers
    @answer = Answer.new
  end

  def new
    @question = Question.new
    @question.answers.new
  end

  def create
    @question = current_user.questions.new(question_params)
    if @question.save
      flash[:notice] = "You have successfully posted your question"
      redirect_to  @question

    else
      @errors = @question.errors.full_messages
      render action: :new
    end
  end

  def edit
    set_question
    @question = Question.find(params[:id]) 
  end

  def update
     @question = Question.find(params[:id])
     @question.update(question_params)
     @answer = @question.answers.new(question_params)
     @question.answers.first.user_id = current_user.id

   if @question.save
     flash[:notice] = "You have sucessfully posted your answer"
     redirect_to @question
   else
      redirect_to new_question_answer_path(@answer), flash: { danger: @question.errors.full_messages.join(",")}
    end
  end

  private

  def set_question
    @question = Question.find(params[:id])
  end

  def question_params
    params.require(:question).permit(:headline, :body, :user_id, :answer, answers_attributes:[:body, :user_id, :question_id])
  end
end

Answers controller:

class AnswersController < ApplicationController
  before_action :find_question

  def index
    @answers = @question.answers
    @user = User.find(@question.user_id)
  end

  def show
    @answer = Answer.find(params[:id])
    @user = User.find(@question.user_id)
  end

  def new
    @answer = Answer.new(:question_id => @question.id)
  end

  def create
    @answer = Answer.new(answer_params)


    if @answer.save
      flash[:notice] = "You have sucessfully created the answer."
      redirect_to(answers_path(@answer, :question_id => @question.id))
    else
      flash[:alert] = "Failed to save the answer."
      @errors = @answer.errors.full_messages
      render :new
    end
  end

  def edit
    @answer = Answer.find(params[:id])
  end

  def update
    @answer = Answer.find(params[:id])
    if @answer.update_attributes(answer_params)
      flash[:notice] = "You have sucessfully updated the answer."
      redirect_to(answer_path(@answer, :question_id => @question.id))
    else
      render :edit
    end
  end

  def delete
    @answer = Asnwer.find(params[:id])
  end

  def destroy
    @answer = Answer.find(params[:id])
    @answer.destroy
    flash[:notice] = "Answer was destroyed"
    redirect_to(answers_path)
  end

  private

   def answer_params
     params.require(:answer).permit(:body, :user_id, :question_id)
   end

   def find_question
     @question = Question.find(params[:question_id])
   end

end

My routes file looks like this:

    Rails.application.routes.draw do

  get 'questions/index'



  root to: 'questions#index'

  resources :questions  do
    resources :answers
  end

  devise_for :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

UPDATE: here are the logs from the moment the server was started and the index page displayed to the moment where I go to the questions page and log the answer

   rails server
=> Booting Puma
=> Rails 5.2.2 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.0 (ruby 2.6.0-p0), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/questions/11/answers" for 127.0.0.1 at 2019-03-07 16:10:13 +0600
   (1.4ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
  ↳ /Users/irina/.rvm/gems/ruby-2.6.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Processing by AnswersController#index as HTML
  Parameters: {"question_id"=>"11"}
  Question Load (0.8ms)  SELECT  "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT $2  [["id", 11], ["LIMIT", 1]]
  ↳ app/controllers/answers_controller.rb:65
  User Load (2.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/answers_controller.rb:6
  Rendering answers/index.html.erb within layouts/application
  Answer Load (0.9ms)  SELECT "answers".* FROM "answers" WHERE "answers"."question_id" = $1  [["question_id", 11]]
  ↳ app/views/answers/index.html.erb:11
  User Load (1.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/views/answers/_new.html.erb:7
  Rendered answers/_new.html.erb (61.7ms)
  Rendered answers/index.html.erb within layouts/application (92.7ms)
[Webpacker] Compiling…
Started GET "/questions/11/answers" for 127.0.0.1 at 2019-03-07 16:10:18 +0600
Processing by AnswersController#index as HTML
  Parameters: {"question_id"=>"11"}
  Question Load (1.4ms)  SELECT  "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT $2  [["id", 11], ["LIMIT", 1]]
  ↳ app/controllers/answers_controller.rb:65
  User Load (1.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/answers_controller.rb:6
  Rendering answers/index.html.erb within layouts/application
  Answer Load (1.3ms)  SELECT "answers".* FROM "answers" WHERE "answers"."question_id" = $1  [["question_id", 11]]
  ↳ app/views/answers/index.html.erb:11
  User Load (1.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/views/answers/_new.html.erb:7
  Rendered answers/_new.html.erb (9.3ms)
  Rendered answers/index.html.erb within layouts/application (18.5ms)
Completed 200 OK in 133ms (Views: 108.3ms | ActiveRecord: 18.4ms)


[Webpacker] Compilation failed:

Hash: 53a953077891e4cef2e8
Version: webpack 3.12.0
Time: 2928ms
                                  Asset       Size  Chunks             Chunk Names
    application-c57a289721a93641de38.js     3.1 kB       0  [emitted]  application
application-c57a289721a93641de38.js.map    2.49 kB       0  [emitted]  application
                          manifest.json  142 bytes          [emitted]
   [0] ./app/javascript/packs/application.js 346 bytes {0} [built] [failed] [1 error]

ERROR in ./app/javascript/packs/application.js
Module build failed: SyntaxError: Unexpected token (14:15)

  12 |     if(window.railsEnv && window.railsEnv === 'development'){
  13 |       try {
> 14 |         render(<App />, reactElement)
     |                ^
  15 |       } catch (e) {
  16 |         render(<RedBox error={e} />, reactElement)
  17 |       }


Completed 200 OK in 11715ms (Views: 11626.9ms | ActiveRecord: 27.7ms)


Started PATCH "/questions/11" for 127.0.0.1 at 2019-03-07 16:10:41 +0600
Processing by QuestionsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"q7HQt4uGPwBIIz0icswfJLWMRk6MiopIfWu9JBcjkuX1VpGBdwlwZu903NDuebSaX8Y90VHnvcEoaV8unV2zkw==", "question"=>{"answer"=>{"answer_attributes"=>"This is the test answer to see how the information goes through", "user_id"=>"3", "question_id"=>"11"}}, "commit"=>"Post Your Answer", "id"=>"11"}
  User Load (0.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ /Users/irina/.rvm/gems/ruby-2.6.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
  Question Load (0.5ms)  SELECT  "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT $2  [["id", 11], ["LIMIT", 1]]
  ↳ app/controllers/questions_controller.rb:55
  CACHE Question Load (0.0ms)  SELECT  "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT $2  [["id", 11], ["LIMIT", 1]]
  ↳ app/controllers/questions_controller.rb:39
Unpermitted parameter: :answer
   (0.5ms)  BEGIN
  ↳ app/controllers/questions_controller.rb:40
  User Load (0.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/questions_controller.rb:40
  Answer Load (0.5ms)  SELECT "answers".* FROM "answers" WHERE "answers"."question_id" = $1  [["question_id", 11]]
  ↳ app/controllers/questions_controller.rb:40
   (0.3ms)  COMMIT
  ↳ app/controllers/questions_controller.rb:40
Unpermitted parameter: :answer
   (0.3ms)  BEGIN
  ↳ app/controllers/questions_controller.rb:44
  CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/questions_controller.rb:44
   (0.4ms)  ROLLBACK
  ↳ app/controllers/questions_controller.rb:44
Completed 500 Internal Server Error in 97ms (ActiveRecord: 8.3ms)



ActionController::UrlGenerationError (No route matches {:action=>"new", :controller=>"answers", :question_id=>nil}, missing required keys: [:question_id]):

app/controllers/questions_controller.rb:48:in `update'

UPDATE NO 2. It looks like because of this falsely set @answer the @question does not get saved as intended and the second part of the conditional kicks redirecting to the new_question_answer_path. I tried to update it to edit_question_answer_path and it gives the same error that no route matches.

If I open the answer in Pry I get the following object:

[1] pry(#<QuestionsController>)> @answer
=> #<Answer:0x00007fc3ec823c98
 id: nil,
 body: nil,
 question_id: 11,
 user_id: 3,
 selected: nil,
 created_at: nil,
 updated_at: nil>

UPDATE No 3 Looks like changing my routes.rb to

 Rails.application.routes.draw do
  resources :questions, :has_many => :answers
  root to: 'questions#index'
  resources :questions  do
      resources :answers
    end
  devise_for :users
end

and also changing the form for the answer to this

<h2> Your Answer </h2>


<%= form_for [@question, Answer.new] do |form| %>

     <div class="form-group">
        <%=form.text_area :body, placeholder: "Add your answer", rows:10, class:"form-control" %><br>

         <%=form.hidden_field :user_id, value: current_user.id, class:'d-none' %>
         <%=form.hidden_field :question_id, value: @question.id %>
      </div>

    <div>
       <%=form.submit 'Post Your Answer', class: 'btn-primary' %>
   </div>
<% end %>

did the trick and helped to fix the problem. I am not sure if this is a perfect fix though)

iseitz
  • 56
  • 1
  • 6
  • 1
    We need to see your server logs to see how the params are being passed. Copy and paste whatever the server outputs after you hit submit – Mark Mar 07 '19 at 09:59
  • One thing is strange is that you build a form for the question where you only query for an answer. Why don't you only build a form for the answer below the preexisiting question. Because you already pass the question ID as a hidden field, you can easily associate the answer to the right question ... One little side thing: you make Answer a child of User, but you don't need to do that as the answer is already a child of question which itself is a child of user. You can easily find the user with any given answer. – Maxence Mar 07 '19 at 11:08
  • It looks like updating the route.rb and editing the form did the trick and the app works. I added the new route.rb and view/answers/_new.html.erb to my question in the form of an update NO 3. – iseitz Mar 11 '19 at 10:51
  • This video appeared to be very helpful http://railscasts.com/episodes/154-polymorphic-association?autoplay=true – iseitz Mar 11 '19 at 10:55
  • Maxence, thank you for making me think in the right direction I believe that the form was not set properly. At the same time, the structure of the app expects to have one user asking the question and any other user answering to it so I can't see how I would trace back the user ID for the answer from the question. It is my bad that I did not clarify this in my question. – iseitz Mar 11 '19 at 11:00
  • Mark, I pasted the server log in the update to my question. At the same time I believe I fixed the problem. Not perfectly though) Thank you for taking time to look into this question. – iseitz Mar 11 '19 at 11:02

1 Answers1

0

Looks like changing my routes.rb to

  Rails.application.routes.draw do
      resources :questions, :has_many => :answers
      root to: 'questions#index'
      resources :questions  do
          resources :answers
      end
      devise_for :users
   end

and also changing the form for the answer to this

 <%= form_for [@question, Answer.new] do |form| %>
     <div class="form-group">
         <%=form.text_area :body, placeholder: "Add your answer", rows:10, 
         class:"form-control" %><br>
         <%=form.hidden_field :user_id, value: current_user.id, class:'d-none' %>
         <%=form.hidden_field :question_id, value: @question.id %>
      </div>
      <div>
          <%=form.submit 'Post Your Answer', class: 'btn-primary' %>
      </div>
 <% end %>

did the trick and helped to fix the problem. I am not sure if this is a perfect fix though)

iseitz
  • 56
  • 1
  • 6