2

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
  • I've read your question a few times now and I honestly don't understand what you're asking. What update_parameters method? Commented Dec 17, 2013 at 18:53
  • Oops, I meant the update_attributes method attached to @exam. Sorry about that. Fixed it in an edit. Commented Dec 17, 2013 at 22:12

2 Answers 2

1

@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.

Sign up to request clarification or add additional context in comments.

5 Comments

Ah, that makes sense. So, I need to add a def problem_ids= method to the exam model? Inside that method, I guess I would just put what I already have: @exam.problems.clear some_problem_ids.each { |prob_id| @exam.problems << Problem.find(prob_id) } And the strong_parameters thing you're talking about is where I put the .permit stuff, right?
The association creates a problem_ids setter method for you, and yes, you would need to add the strong parameters (.permit stuff) if you're using Rails 4
I've created that problem_ids= method - written out in my update above. @exam.update_attributes(params[:exam].permit(:title, :problem_ids) still doesn't do anything to the problems list, though. Now, I have to explicitly call @exam.problem_ids = params[:exam][:problem_ids]. Any idea why? Does it have something to do with the fact that I had to use check_box_tag in my view instead of builder.check_box?
No, you don't need to create the problem_ids method. This should be on your model as a result of the has_many association. Please read more about them at guides.rubyonrails.org/association_basics.html
Oh, ok. So, why isn't that method being called by my @exam.update_attributes(params[:exam].permit(:title, :problem_ids)?
0

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!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.