azangru
azangru

Reputation: 2748

Nested model form in Rails

I am trying to create a web app that could be used for submitting answers to quizes. And I am having troubles creating the view files.

The submission form should look like this (sorry, SO does not allow me to insert images in the body of the question).

It is a case of nested model forms discussed by Ryan Bates in Railscasts, but there is an important difference. In Ryan Bates' case there is a simple chain of associations: a Survey model has many Questions, and each Question has many Answers.

What I am planning, though, is a bit more complex. In my case, a Quiz has many Questions, and a Question has many Answers, but what a user is presented with is a model called Submission. A Submission is associated with the User model and the Answer model, and it also has some additional information. Schematically, my associations look like this:

So I need to create a form that will combine a model (Submission) with many unassociated models (Questions) and many associated models (Answers). All I could think of for my new/edit view file is this:

Controller:

  # GET /tests/:test_id/submissions/new
  def new
    @quiz = Quiz.find(params[:quiz_id])
    # the next four lines of code are to select a subset of questions
    variant = choose_variant(@quiz)
    @submission = Submission.new
    @submission.variant = variant
    @questions = Question.where (variant: variant)
    @questions.each {|question| quiestion.answers.build}
  end

View:

= form_for [@quiz, @submission]  do |f|

    = f.fields_for :answers do |builder|
      = @questions.each do |question|
      %p
        = @question.text
       = builder.label :answer_text, "Your answer"
       = builder.text_area :answer_text

But it doesn't look right: there is no association between the @submission, the @question and the answers. Could you please suggest how to solve this problem if there is no direct association between the Submission and the Question model?


UPDATE:

I can now create a form in the view:

= form_for [@quiz, @submission]  do |f|

    - @questions.each do |question|
      %p
        = question.text
      = f.fields_for :answer, question.answers.first do |builder|
        = builder.label :text, "Answer"
        = builder.text_area :text

The trouble is, on submit the last answer overwrites all the privious answers, and in the params I get only:

"submission"=>{"answer"=>{"text"=>"Answer to the last question"}}


UPDATE 2:

To Marcelo Risoli (sorry, I haven't got the hang of StackOverflow behavior, and it doesn't allow me to write another comment). I apologize, I did not quite understand your suggestion at first. Your line @questions.each { |q| @submission.answers.build question_id: q.id } is pure genius: it associates answers with the submission in the controller instead of in the view, as I initially tried to do. Will try do go through with your suggestion and will write back. But it seems like this line will solve all my problems :-)

Upvotes: 0

Views: 617

Answers (2)

Marcelo Risoli
Marcelo Risoli

Reputation: 802

I did something very similar, I can give you some suggestions:

First, I'd advise you use cocoon, nested form is not being updated anymore, and I found cocoon to be much easier to deal with.

Second, the submission does not need association with the question, if necessary you can access a question with a given submission with the a has_many: :through association.

When you use fields_for :answers in a form_for submission means the newly created submission_id will be input into the answers, now all you need is to input the question_id, you can do that through a hidden input, just do the following in your controller:

@questions.each { |q| @submission.answers.build question_id: q.id }

then add = builder.input :question_id, as: :hidden to your form

Upvotes: 0

Michael Durrant
Michael Durrant

Reputation: 96594

Use appropriate join tables. Suggest you use an 'answer' table for the actual answer that a user makes. Use question_options and option_groups for the possible answers for a question.

Suggest you consider basing it on https://stackoverflow.com/a/5858666/631619

Just substitute 'survey' for 'quiz'

enter image description here

Upvotes: 1

Related Questions