Question

Ruby on Rails 4

Want form to create Test record which references Questions. So a Test has many questions and a Question has many tests. When the form submits it is creating the Test record but is saying the question parameter is unpermitted. But also the question param shouldn't be saved to Test it should be saved to my questions_tests table. How do you do this? That way I can show a Test with the questions that were chosen by the form.

Models:

class Test < ActiveRecord::Base
  has_many :questions_tests
  has_many :questions, :through => :questions_tests
  accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
  has_many :answers, dependent: :destroy
  has_many :test_questions
  has_many :tests, :through => :questions_tests
end
class QuestionTest < ActiveRecord::Base
  belongs_to :test
  belongs_to :question
end

Controllers:

#tests_controller.rb
def create
respond_to do |format|
    if test = Test.create(test_params)
      @test = []
      @test << test
      question_test = @test.find(test.id)
      format.html { redirect_to @test, notice: 'Test was successfully created.' }
      format.json { render action: 'show', status: :created, location: @test }
    else
      format.html { render action: 'new' }
      format.json { render json: @test.errors, status: :unprocessable_entity }
    end
  end
end
def test_params
    params.require(:test).permit(:name, :user_id, :type, :category, :description, :question, question_attributes: [ :id, :content, :category ] ).
    merge user_id: current_user.id
end  

Form:

<%= form_for(@test) do |f| %>
<div class="field">
  <%= f.label :name %><br>
  <%= f.text_field :name %>
</div>
<div class="field">
  <%= f.label :type %><br>
  <%= f.text_field :type %>
</div>
<div class="field">
<%= f.label :category %><br>
<%= f.select :category, [ ["one", "one"], ["two", "two"] ], {prompt: "Select Category"}, class: "input-lg" %>
</div>
<div class="field">
  <%= f.label :description %><br>
  <%= f.text_field :description %>
</div>
<div class="field">
  <%= f.label :question %><br>
  <%= f.fields_for :question do |question| %>
  <%= question.collection_select :question_id, Question.where({ category: "ip_voice" }), :id, :content, {prompt: "Select Question"}, {class: "form-control input-lg"} %>
<% end %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

Log:

Started POST "/tests" for x at 2014-05-07 14:34:59 -0400
Processing by TestsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x=", "test"=>{"name"=>"thoo", "type"=>"", "category"=>"one", "description"=>"chickens on the road", "question"=>{"question_id"=>"1"}}, "commit"=>"Create Test"}
Unpermitted parameters: question
User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'ca9a73ab72a94f538723de34856a92dcde068e7b' LIMIT 1
(0.0ms)  begin transaction
SQL (0.3ms)  INSERT INTO "tests" ("category", "created_at", "description", "name", "type", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?)  [["category", "ip_voice"], ["created_at", Wed, 07 May 2014 18:34:59 UTC +00:00], ["description", "chickens on the road"], ["name", "thoo"], ["type", ""], ["updated_at", Wed, 07 May 2014 18:34:59 UTC +00:00], ["user_id", 1]]
(17.1ms)  commit transaction
Redirected to http://0.0.0.0:3000/tests/11
Completed 302 Found in 40ms (ActiveRecord: 17.7ms)
Was it helpful?

Solution

A Test has_many questions.

You would need to update the test_params method to reflect that:

def test_params
    params.require(:test).permit(:name, :user_id, :type, :category, :description, questions_attributes: [ :id, :content, :category ] ).
    merge user_id: current_user.id
end

You don't need :question here unless you have an attribute named question in tests table. Also, you should be using questions_attributes (Notice plural questions) and NOT question_attributes (Notice singular question)

On the same lines, you need to update the fields_for in your view as below:

<%= f.fields_for :questions do |question| %>

:questions (Notice plural questions) and NOT :question (Notice singular question)

UPDATE

The join model of Test and Question is named QuestionTest so you should be referring it in associations as question_tests (For has_many)

You have incorrectly setup the associations, update them as below:

class Test < ActiveRecord::Base
  has_many :question_tests  ## "question_tests" and NOT "questions_tests"
  has_many :questions, :through => :question_tests  ## "question_tests" and NOT "questions_tests"
  accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
  has_many :answers, dependent: :destroy
  has_many :question_tests   ## "question_tests" and NOT "test_questions"
  has_many :tests, :through => :question_tests   ## "question_tests" and NOT "questions_tests"
end

Implemented Solution: Hope this helps anyone needing similar results.

tests_controller.rb

def new
  @test = Test.new
end
def create
  @test = Test.new(test_params)
  @test.save
end

_form.html.erb

<%= form_for(@test) do |f| %>
  ...
  <div class="field">
 <%= f.label :question %><br>
    <%= f.collection_check_boxes :question_ids, Question.all.to_a.collect, :id, :content %>
  </div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

Schema for join table, ensure you don't have id:false:

create_table "question_tests", force: true do |t|
  t.integer "test_id",     null: false
  t.integer "question_id", null: false
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top