abhi-axis
abhi-axis

Reputation: 149

How to save selected radio button value in model

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

Answers (2)

joem
joem

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

Richard Peck
Richard Peck

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:

  1. MCQ (collection of questions & possible answers)
  2. Applications (user provides details such as why wanting to join etc)
  3. ApplicationAnswers (user's answers to questions. Separate model to provide extensibility (IE can add as many answers per question)
  4. 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

Related Questions