ruby on rails, can't pass check_box_tag values back to update_attributes

793 views Asked by At

I have a fairly simple Rails application that does things with math quizzes. Users can create math problems with a question and an answer. They can also create exams with a title. The models are linked with has_many_and_belongs_to relationship because each quiz can have many problems, and each problem can belong to many quizzes. That relationship seems to be working well in that when I delete problems or exams, the related object lists are updated. The way I have it currently is that, when you're creating an exam, the view gives you a list of check boxes, one for each problem in the problem database. The user clicks the check boxes for the problems they want in their exam, and whichever boxes are checked, those problems are added to the exam. I'll show the code for the update, since that's the view I've been using to test with.

Here's the view:

  <h2>Available Problems:</h2>
  <% Problem.all.each do |prob| %>
    <%= f.fields_for "exam[problem_ids][]", prob do |builder| %>
      <p>
      <%= check_box_tag "exam[problem_ids][]", prob.id, @exam.problems.include?(prob) %>
      <%= prob.question %> = <%= prob.answer %> | <%= link_to "Edit", edit_problem_path(prob) %>
      </p>
    <% end %>
  <% end %>

I had to use check_box_tag because if I tried to use builder.check_box, it wouldn't save which boxes were already checked.

My controller:

  def update
    @exam = Exam.find(params[:id])

    if @exam.update_attributes(params[:exam].permit(:title, :problem_ids))
      redirect_to @exam
    else
      render 'edit'
    end
  end

In the view, I saved all the checkbox results in the problem_ids array. I know this array is being filled correctly, because when I add the following code, it works.

@exam.problems = []
params[:exam][:problem_ids].each {
  |prob_id|
  @exam.problems << Problem.find(prob_id)
}

Obviously, this is a very hackey way to do it, since I manually build the problem list each time, but I just can't for the life of me figure out how to get those checkbox results to automatically populate in the update_attributes method. Any ideas?

UPDATE: In accordance to Donovan's suggestion below, I implemented the following method in my exam model:

  def problem_ids=(some_problem_ids)
    @exam.problems.clear
    some_problem_ids.each {
      |prob_id|
      @exam.problems << Problem.find(prob_id)
    }
  end

As I understand it, this is supposed to be a "setter" method for problem_ids. Sadly, the problem list is still not updated when I use the following line in my exam controller:

@exam.update_attributes(params[:exam].permit(:title, :problem_ids))

P.S. I have to put the .permit(:title, :problem_ids) bit or else I'll get a ForbiddenAttributesError.

2

There are 2 answers

0
Onikoroshi On BEST ANSWER

Thank you for all your help, but I finally figured it out on my own. The problem was that, even though I was allowing problem_ids to be passed as a parameter, I wasn't telling the controller that it was actually an array. So, once I changed the relevant line to:

@exam.update_attributes(params[:exam].permit(:title, problem_ids: []))

it worked perfectly!

5
Donovan On

@exam.problems is a list of objects but there should also a problem_ids setter method on your exam due to the relationship, so

@exam.update_attributes(params[:exam])

should do what you want. If you need to only update the problem_ids for some reason, this should do the trick:

@exam.update_attribute(:problem_ids, params[:exam][:problem_ids])

Note: I left out the rails4 strong_parameters to make the answer generic for rails 3 or 4, make sure you use the appropriate methods for your version.