Reputation: 1173
I'm trying to create a quiz builder where a user builds a quiz that is added to the Quiz
model, a question that is in the QuizQuestions
model and answer choices in and AnswerChoices
model.
The Quiz is the main controller. From the edit page, the user can add a new question and select an answer choice type The question model is nested under the quiz model. Then the answer model is nested under the quiz model.
However, saving the form does not save the answer choices to the database, and there is an unpermitted parameter
message, but all parameters are permitted, and it happens even with one paramter.
The controller looks like:
def edit
@quiz = Quiz.find(params[:id])
@quiz_questions = @quiz.quiz_questions.all
@quiz.quiz_questions.build(quiz_params) unless @quiz.quiz_questions.present?
end
def update
@quiz = Quiz.find(params[:id])
@quiz_questions = @quiz.quiz_questions
if @quiz.update(quiz_params)
redirect_to edit_quiz_path, notice: "Successfully upated quiz"
else
redirect_to admin_path, notice: "UNSUCCESSFULLY added quiz"
end
end
def quiz_params
params.require(:quiz).permit(
:quiz_name,
quiz_questions_attributes: [:id, :question, :quiz_id],
quiz_answers_attributes: [:id, :question_id]
)
end
The models (in different files):
class Quiz < ApplicationRecord
#Associations
has_many :quiz_questions, foreign_key: :quiz_id
accepts_nested_attributes_for :quiz_questions
end
class QuizQuestion < ApplicationRecord
#ASSOCIATIONS
belongs_to :quiz, foreign_key: :quiz_id
has_many :quiz_answers, foreign_key: :question_id
accepts_nested_attributes_for :quiz_answers, allow_destroy: true
end
class QuizAnswer < ApplicationRecord
#Associations
belongs_to :quiz_question, foreign_key: :question_id
end
quiz/edit.html.erb
<div >
<%= form_with model: @quiz do |newQuiz| %>
<div >
<h3> <%= newQuiz.label :quiz_name, "QUIZ: " %></h3>
<%= newQuiz.text_field :quiz_name, %>
</div>
<%= newQuiz.fields_for :quiz_questions do |questionData| %>
<div>
<h3> <%= questionData.label :question, "Question" %></h3>
<%= questionData.text_field :question %>
</div>
<%= questionData.hidden_field :quiz_id, value: 1 %>
<%= newQuiz.fields_for :quiz_answers do |answerData| %>
<%= answerData.text_field :id %>
<%= answerData.hidden_field :question_id, value: 1 %>
<% end %>
<% end %>
</div>
<%= newQuiz.submit "SAVE" %>
<% end %>
</div>
The error says:
Unpermitted parameter: :quiz_answers. Context: { controller: QuizzesController, action: update, request: #<ActionDispatch::Request:...>, params: {"_method"=>"patch", "authenticity_token"=>"[FILTERED]", "quiz"=>{"quiz_name"=>"Test Quiz 1", "quiz_questions_attributes"=>{"0"=>{"question"=>"This is a test question?", "quiz_id" => "1", "id"=>"1"}}, "quiz_answers"=>{"id"=>"1", "question_id"=>"1"}}, "commit"=>"SAVE", "controller"=>"quizzes", "action"=>"update", "id"=>"1"} }
Upvotes: 1
Views: 41
Reputation: 2089
Basically you are nesting your models in 3 levels (A Quiz
contains multiple QuizQuestion
s, and a QuizQuestion
contains multiple QuizAnswer
s.
So to use the accepts_nested_attributes
option, your input parameters should also be nested accordingly, e.g. your quiz_params
are supposed to look like
def quiz_params
params.require(:quiz).permit(
:quiz_name,
quiz_questions_attributes: [
:id,
:question,
:quiz_id,
quiz_answers_attributes: [:id, :question_id]
],
)
end
The view should be build accordingly, that you have some input fields for the answers for every single question the user enters, e.g.
<div>
<%= form_with model: @quiz do |newQuiz| %>
<div>
<h3> <%= newQuiz.label :quiz_name, "QUIZ: " %></h3>
<%= newQuiz.text_field :quiz_name, %>
</div>
<%= newQuiz.fields_for :quiz_questions do |questionData| %>
<div>
<h3> <%= questionData.label :question, "Question" %></h3>
<%= questionData.text_field :question %>
</div>
<%= questionData.hidden_field :quiz_id, value: 1 %>
<%= questionData.fields_for :quiz_answers do |answerData| %>
<%= answerData.text_field :id %>
<%= answerData.hidden_field :question_id, value: 1 %>
<% end %>
<% end %>
<%= newQuiz.submit "SAVE" %>
<% end %>
</div>
On a sidenote, you should work a bit of your formatting, you are actually closing one div more than you are opening, which is a bit difficult to spot in the code as you wrote it...
Upvotes: 1