Reputation: 149
I have a assessment model like Rails Survey Gem (runtimerevolution), there teacher create a MCQ assessment for student. Its Options are in type of radio button. there one option is correct.
when a student apply for a MCQ assessment, it get a question paper where Question has multiple options and student select one option through radio button.
So I just want to store that selected option_id regarding question_id in my new model attempt.
Here is apply page of MCQ for student.
<div class="stream-cont">
<%= form_tag(action: "apply") do %>
<% @mcq.questions.each_with_index do |q, i| %>
<div class="feed-cont-title all-header">
<p>
<%="Question-#{i+1}: "+ q.question %>
</p>
<p>
<strong>Answer : </strong>
<%=
collection_radio_buttons("questions[#{q.id}]", :option_id, q.options, :id, :option) do |b|
b.label(style: "display:block") { b.radio_button + b.text }
end
%>
</p>
</div>
<% end %>
<% end %>
<%= submit_tag "Submit Answer", :class => "typeD button" %>
</div>
here is Mcq controller
def apply
@club = current_user.clubs
if current_user.user_type == User::User_types[:student]
@mcq = Mcq.find(params[:id])
end
@attempt = current_user.attempts.new
@mcq = Mcq.find(params[:id])
@attempt = current_user.attempts.new(attempt_params)
@attempt.save
redirect_to attempt_id(@attempt.id), notice: "Thank you"
end
Private
def attempt_params
params.require(:attempt).permit(:user_id, :mcq_id, :question_id, :option_id)
end
end
it is not complete and I know it is incorrect. I just want to know how I get selected Option_id regarding question_id and save this value in attempt model's option_id column.
I have know idea how to manage it and what should use in view and controller.
Upvotes: 1
Views: 1038
Reputation: 331
The Mcq is actually the top level model here, not Attempt, so I think the nesting is causing a bit of confusion.
You can leverage form_for and fields_for in this situation, which will nest the params hash for you. It would look something like this:
<%= form_for @mcq do |f| %>
<% f.fields_for :question |question_fields, i| %>
<div class="feed-cont-title all-header">
<p>
<%="Question-#{i+1}: "+ question_fields.object %>
</p>
<p>
<strong>Answer : </strong>
<% question_fields.fields_for :option |option_fields| %>
<%= option_fields.radio_button(:id) %>
<%= option_fields.label(:name) %>
<% end %>
</p>
</div>
<% end %>
<% end %>
You then want your controller to whitelist the params like:
Private
def mcq_params
params.require(:mcq).permit(:user_id, :mcq_id, questions_attributes[ :id, :options_attributes[:id]])
end
When you submit the form, your params will look something like
:mcq[:user_id, :id, questions_attributes[][:id, :options_attributes[:id]]
At that point, you just iterate through the questions attributes hash to build the attempts objects on the user.
Sorry if the code is not spot on - I am not sure I completely understand the relationship between question and options, but this should get you headed down the path. If it is too nesty, you can keep the radio buttons collection, I just find that over time, forms tend to get complicated so it's kind of nice to have rails nest them for you.
Upvotes: 0
Reputation: 76774
Firstly, why do you have an apply
method?
Rails relies heavily on the CRUD
infrastructure; your request should go into an applications
controller, tied to an Application
model:
#app/models/application.rb
class Application < ActiveRecord::Base
#columns id | mcq_id | user_id | created_at | updated_at
belongs_to :mcq
belongs_to :user
has_many :answers, class: "ApplicationAnswer"
accepts_nested_attributes_for :answers
end
#app/models/application_answer.rb
class ApplicationAnswer < ActiveRecord::Base
#columns id | application_id | question_id | answer_id | created_at | updated_at
belongs_to :application
belongs_to :question
belongs_to :answer
end
#app/models/mcq.rb
class MCQ < ActiveRecord::Base
has_many :applications
has_many :questions
has_many :answers, through: :questions
end
#app/models/question.rb
class Question < ActiveRecord::Base
#columns id | mcq_id | etc | etc
belongs_to :mcq
has_many :answers
accepts_nested_attributes_for :answers #-> you can make a nested form; each time you make a question, you can make answers
end
#app/models/answer.rb
class Answer < ActiveRecord::Base
#columns id | question_id | value | created_at
belongs_to :question
end
Looks complicated; all it does is split up your data as follows:
MCQ
(collection of questions & possible answers)Applications
(user provides details such as why wanting to join etc)ApplicationAnswers
(user's answers to questions. Separate model to provide extensibility (IE can add as many answers per question)Questions
(tied to MCQ; provides ability to add a question & assign answers to it).
--
This means that you can do the following:
#config/routes.rb
resources :mcqs, only [:show] do
resources :applications, only: [:new, :create] #-> url.com/mcq/:id/applications/new
end
#app/controllers/applications_controller.rb
class ApplicationsController < ApplicationController
def new
@mcq = MCQ.find params[:id]
end
def create
@mcq = MCQ.find params[:id]
@application = @mcq.applications.new application_params
if @application.save
# ...
else
# ...
end
end
private
def application_params
params.require(:application).permit(:application, :params, :mcq_id, answer_params: [:question_id, :application_id, :answer_id]).merge(user_id: current_user.id)
end
end
Now, the form you're looking to create has the ability to save ApplicationAnswers
through your Application
:
#app/views/applications/new.html.erb
<%= form_for @mcq.applications.new do |f| %>
<%= f.text_field :your_mcq_stuff_here %>
<%= f.fields_for :answers, @mcq.questions do |question| %>
<%= question.hidden_field :question_id, question.id %>
<%= question.collection_radio_buttons :answer_id, question.answers.all, :id, :value %>
<%= f.submit %>
<% end %>
<% end %>
Several issues here include the n+1
query issue, and the fact I've not tested it.
--
If this works properly, you'll basically be able to give users
the ability to submit an Application
to an MCQ
. Each MCQ
will have a series of Questions
, those questions having a series of Answers
.
This will probably need tweaking; it's the structure I'd use for this type of project.
Upvotes: 1