On my questions index page i have a list of all the questions. I want to be able to answer each question right on the page by rendering the answers form. I am getting this error when i try and do that:
undefined method `answers' for #<Question:0x00000103dc0100>
It highlights the second line of the answers controller:
def create
#question = Question.find(params[:question_id])
#answer = #question.answers.new(answer_params)
#answer.save
end
Here is the view:
<% #questions.each do |question| %>
<%= question.body %>
<%= render :partial => "answers/form", locals: {question:question} %>
<% end %>
And the form looks like this:
<%= simple_form_for [question, Answer.new] do |f| %>
<%= f.input :body %>
<% end %>
Lastly, the questions controller:
def index
#questions = #comment.questions.order
#answer = answer.new
end
Since you have a belongs_to has_one relationship, the correct association is this:
Question.find(params[:question_id]).answer
Note that answer is singular. This is because each Question has only one Answer - thus, instances of Question do not have the method answers, as indicated in the exception.
If you wanted each question to have multiple answers, you'd define the following associations:
# app/models/question.rb
class Question < ActiveRecord::Base
has_many :answers
end
# app/models/answer.rb
class Answer < ActiveRecord::Base
belongs_to :question
end
With a belongs_to has_many relationship, you'd be able to access multiple answers on each question as follows:
Question.find(params[:question_id]).answers
UPDATE 1:
There are a couple ways to add an Answer to a Question.
Option 1: Utilizing the Rails build method as made available by the has_one association:
Question.find(params[:question_id]).build_answer(answer_params)
Option 2: Directly assign an Answer to a Question:
answer = Answer.first
question = Question.first
question.answer = answer
In either method, note that, because each Question is limited to a single Answer, a question's existing answer will be replaced, rather than added to.
UPDATE 2:
Here's how your controller action should look in its entirety utilizing each of the suggested methods:
Option 1:
# app/controllers/answers_controller.rb
def create
#question = Question.find(params[:question_id])
#question.build_answer(answer_params)
end
Option 2:
# app/controllers/answers_controller.rb
def create
#answer = Answer.new(answer_params)
#question = Question.find(params[:question_id])
#question.answer = #answer
end
If you have belongs_to question, and has_one :answer, then you can only do the following:
Question.find(1).answer
not
Question.find(1).answers
which would be a has_many relationship
EDIT
#answer = answer.new
is almost definitely not what you want.
You want
#answer = Answer.new
Or
#answer = #question.answers.new
Or
#answer = #question.answer.new
It seems like you have not set your associations in your model. Basically, Rails is looking for a method called "answers" in your Question model. Have you set belongs_to :question in your Answer model and has_many :answers in your Question model? Can't tell if that's the real issue without seeing your model code.
More info on associations: http://guides.rubyonrails.org/association_basics.html
Edit - since you have a has_one association, the syntax is a bit different.
Try changing "answers" to "answer" - see the table on generated methods for different association types in this doc: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001834
Related
I have a survey application that I confused on final side.
here is app models properties:
User
- Survey
user_id
title
- Question
title
survey_id
type: multiple_choice|check_boxes|short_answer
- Option
title
question_id
(Till here is okay. I can create surveys these includes more nested forms) Issue is after created surveys. (users responses)
-Response
user_id
survey_id
-Answer
question_id
response_id
option_id
Creating survey with nested attributes is okay. Problem is at Response side. how should be my response controller and response form on Survey show.html.erb?
There is response controller nested attributes below;
def response_params
params.require(:response).permit(:id, :user_id, :survey_id, answers_attributes:[:question_id, :response_id, :option_id ] )
end
I should tell that survey can includes multiple questions these with only radio_buttons (independent radio buttons are other issue)
This issue made me so tired. I'll be glad if you can help me. Thanks.
For Source code: Click for source codes
updated files:
Response model:
class Response < ApplicationRecord
belongs_to :user
belongs_to :survey
has_many :answers, dependent: :destroy
validates :survey, presence: true
counter_culture :option
accepts_nested_attributes_for :answers
end
Survey_controller:
def new_response
#survey = Survey.find(params[:id])
#response = #survey.responses.build
# now, the tricky part, you have to build the Answer objects so you can use the nested form later
#survey.questions.each do |q|
#response.answers.build question: q
end
end
def create_response
#survey = Survey.find(params[:id])
#response = #survey.build(response_params)
#response.user = current_user
#response.save
end
Routes:
Rails.application.routes.draw do
devise_for :users
resources :surveys do
member do
get :new_response
get :create_response
end
end
root 'surveys#index'
end
form:
- # surveys/new_response.html.haml
- # You need to define a nested route inside survey resource to create the response
= form_for #response, url: create_response_survey_path(#survey) do |f|
- # you can iterate over all the answers already initialized
= f.fields_for :answers do |ff|
- # get the question from the current answer to show the title and options and a hidden_field with the question id
- q = ff.object.question
= q.title
= ff.hidden_field :question_id
- # add the radios for each options for the question
- q.options.each do |option|
= label_tag do
= ff.radio_button :option_id, option.id
= option.title
= f.submit 'Send'
I wouldn't use the Survey's show action to show the form to create a Response, I think it's better to approach it as a new_response action to make it cleaner and leave the show action just to show the actual survey (not to respond it). Something like:
class SurveysController < ApplicationController
def new_response
#survey = Survey.find(params[:id])
#response = #survey.responses.build
# now, the tricky part, you have to build the Answer objects so you can use the nested form later
#survey.questions.each do |q|
#response.anwers.build question: q
end
end
Now, you can have a form for the response:
- # surveys/new_response.html.haml
- # You need to define a nested route inside survey resource to create the response
= form_for #response, url: create_response_survey_path(#survey) do |f|
- # you can iterate over all the answers already initialized
= f.fields_for :answers do |ff|
- # get the question from the current answer to show the title and options and a hidden_field with the question id
- q = ff.object.question
= q.title
= ff.hidden_field :question_id
- # add the radios for each options for the question
- q.options.each do |option|
= label_tag do
= ff.radio_button :choice_id, option.id
= option.title
= f.submit 'Send'
Your response_params should be something like:
def response_params
params.require(:response).permit(answers_attributes: [:question_id, :choice_id])
end
note that I removed the :survey_id and the :user_id, you don't want a user to hack your form, change a survey_id or user_id and add responses to another survey made by another user!
and your create_response action:
def create_response
#survey = Survey.find(params[:id])
#response = #survey.build(response_params)
#response.user = current_user
#response.save
end
Hope it makes sense.
I'm using nested attributes in my Ruby on Rails app (4.0.2) such that survey has_many questions and accepts_nested_attributes_for questions and question belongs_to survey.
The problem that I have run into is that when I only want to look at the questions that belong to a particular survey I get an "undefined method "id" for nil:NilClass"-error. When on the other hand I look at all the questions the index action works without problems.
My questions controller index action:
def index
#questions = Question.where(:survey_id => #survey.id).all # if instead I use #questions = Question.all it works fine, but is not what I want.
##questions = #survey.questions.all
#surveys = Survey.all
#survey = Survey.first
end
My surveys/index.html.erb page:
<%= link_to("questions", { :controller => 'questions', :survey_id => survey.id }, :class => 'btn btn-xs') do %>
<%= glyph 'th-list' %>
<%- end -%>
My Question model:
class Question < ActiveRecord::Base
belongs_to :survey
scope :sorted, lambda { order("questions.created_at ASC")}
end
I also use a before_action that I call find_survey which looks like this:
def find_survey
# If in each action calling this method (find_survey) has :survey_id sent
if params[:survey_id]
# We will then go to the database and look for (and find) :survey_id and set that to #survey.
#survey = Survey.find(params[:survey_id])
end
end
Could anybody point me in the right direction?
The undefined method on NilClass is because your instance variable #survey is nil, and when you're calling Question.where(:survey_id => #survey.id).all you're getting that error.
If you're associations are set up right you should be able to run #survey.questions and not have to perform the search on Questions. That's part of ActiveRecord rails magic. You SHOULDN'T have to call #survey.questions.all because that will return all intances of the Question class, leave that off and you should be good to go.
As far as why this isn't working for you now, it may just be an order thing — you're calling #survey before you define it on the line below.
Based on the error you posted, #survey is likely nil in this line:
#questions = Question.where(:survey_id => #survey.id).all
So maybe your "before_action" is not being called?
I'm trying to write a multiple choice quiz using RoR. I'm a beginner so any help is appreciated. So far, I have a Quizzes controller containing the following code:
class QuizzesController < ApplicationController
def new
#quiz = Quiz.new
end
def create
#quiz = Quiz.new(quiz_params)
if #quiz.save
redirect_to "http://www.rubyonrails.org"
end
end
def quiz_params
params.require(:quiz).permit(:question, :wrong_answer_1,
:wrong_answer_2,
:wrong_answer_3,
:correct_answer)
end
def show
#quiz = Quiz.find(params[:id])
end
end
My quiz has five columns (excluding ID) - question, wrong_answer_1, wrong_answer_2, wrong_answer_3, and correct_answer. I'm pretty sure my database/migrations are correct.
I'm really not sure where to go from here. I want the user to have the ability to select, using radio buttons, the correct_answer and the screen to say correct and vice versa with wrong_answer_1, wrong_answer_2, etc.
you already have a new, so you can create questions with fake answers alraedy right.
now you can create a def index, that will display all your questions.
so do:
def index
#questions = Question.all
end
in your index.html.erb file
do something along the lines of:
#questions.each do |q|...
and display them like q.wronganswer, q.wrongaswer2, etc.
you have to figure out a way to display at random the answer options, otherwise they'll all have the same format of
Question
Wrong Answer
Wrong Answer 2
Wrong Answer 3
Correct Answer
I would modify the app to include anwser as a model. Then each question could has_many answers, and one correct answer. the You should use the radio_button_tag
_show_question.html.erb
<% #question.answers.each do |answer| %>
<li>
<%= radio_button_tag("answer[#{question.id}]", answer.id) %>
<%= label("answer_".concat(answer.id.to_s).to_sym, answer.content) %>
</li>
<% end %>
app/models/question.rb
# == Schema Information
#
# Table name: questions
# ...
# correct_answer_id :integer
# ...
has_many :answers, dependent: :destroy
app/models/answer.rb
# == Schema Information
#
# Table name: answers
# ...
# question_id :integer
# ...
belongs_to :question
app/controllers/quizzes_controller.rb
def show
#quiz.includes(:answers).find_by(params[:id])
end
I have the following two models
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
I have an object #post and an array of comments #comments. How can I assign all the comment objects to post in a single line?
#post.comments = #comments
should do what you're asking. Or am I missing something?
#post.update_attributes(:comments => #comments)
OR
#post.comments = #comments ; #post.save
Not sure what exactly do you mean, but maybe this will help:
#post.comments << #comments
The simple answer to your question is
#post.comments = #comments
However you may want to examine carefully how exactly you are creating your comments. It is more likely that comments will need to be created one at a time and in that case you can simply do the following
#post.comments.create!(:body => "foo")
This will add a new comment to your Post
I have gone through tons of the form_for nested resource questions and can't get any of the solutions to work for me. I figured its time to ask a personalized question.
I have two models, jobs and questions, jobs has_many questions and questions belong_to jobs.
I used scaffolding to create the controllers and models then nested the resources in the routes.rb.
root :to => "pages#home"
resources :jobs do
resources :questions
end
get "pages/home"
get "pages/about"
get "pages/contact"
class Job < ActiveRecord::Base
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :job
end
Right now I am trying to access '/jobs/1/questions/new' and keep getting the
NoMethodError in Questions#new
I started with the error No route matches {:controller=>"questions"} when the code was
<%= form_for(#question) do |f| %>
I know this is wrong, so I started to try other combos and none of them worked.
I've tried
<%= form_for([#job.questions.build ]) do |f| %>
that
<%= form_for([#job, #job.questions.build ]) do |f| %>
that
<%= form_for(#job, #question) do |f| %>
Among a bunch of other combinations and that are not working.
Here is a link to my rake routes : git clone https://gist.github.com/1032734
Any help is appreciated and let me know if you need more info, thanks.
I just pass the URL as an extra option:
<%= form_for(#question, :url => job_questions_path(#job)) do %>
EDIT:
Also try:
form_for([#job, #question])
This is how I solved mine :)
In your questions/_form.html.erb
<%= form_for [#job, #question] do %>
For this to work, you need the job's id. You'll pass it as follows:
In the questions_controller.rb
def new
#job = Job.find(params[job_id])
#question = #job.questions.build
end
Build(.build) is similar to using new(.new) in the code above, with differences only in older versions of rails; rails 2 down.
Now for the create action (still in questions_controller.rb)
def create
#job = Job.find(params[:job_id])
#question = #job.questions.build(question_params)
end
If you only use these, the job_id and user_id field in the question model will be empty. To add the ids, do this:
In your questions_controller.rb add job_id to job_params like so:
def question_params
params.require(:question).permit(:ahaa, :ohoo, :job_id)
end
Then to pass the user's id (if you are using Devise), do:
def create
#job = Job.find(params[:job_id])
#question = #job.questions.build(question_params)
#question.user_id = current_user.id
end