如何在 Rails4 中分配深层嵌套属性的强参数?
How to assign strong parameters of deeply-nested attibutes in Rails4?
我正在根据此 Railscast 在 Rails4 应用程序中尝试嵌套对象。模型调查 has_many :questions,模型依次提问 has_many :answers,模型回答 belongs_to :questions 和 questions belongs_to :survey。
现在,这些模型一开始是完全相互分离的,虽然我不希望这样,但效果很好。我更喜欢将它们嵌套在一起,这样我就可以同时分配和显示不同的对象。
然后我不得不弄清楚如何将这些嵌套 objects/attributes 的强参数列入白名单,并且有很好的问题 here 帮助了我。
当我创建数据库条目时,一切正常。当我想编辑数据库中的对象时,问题就来了。在日志中我得到 "Unpermitted parameter: answers" 即使我已经将每个属性列入白名单,包括用于回答的属性。我就是不明白为什么。
谁能给我指出正确的方向?
我的surveys_controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
@surveys = Survey.all
end
def show
@survey = Survey.find(params[:id])
end
def new
@survey = Survey.new
3.times do
question = @survey.questions.build
4.times { question.answers.build }
end
end
def create
@survey = Survey.new(survey_params)
if @survey.save
flash[:notice] = 'Survey was successfully created.'
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
@survey = Survey.find(params[:id])
end
def update
#Find an existing object using form parameters
@survey = Survey.find(params[:id])
#Update the object
if @survey.update_attributes(survey_params)
flash[:notice] = "Survey updated successfully."
#If update succeeds, redirect to 'show' action.
redirect_to(:action => 'show', :id => @survey.id)
else
#Else redisplay the 'edit' form.
render('edit')
end
end
def delete
@survey = Survey.find(params[:id])
end
def destroy
@survey = Survey.find(params[:id]).destroy
flash[:notice] = "Survey destroyed successfully."
redirect_to(:action => 'index')
end
private
def set_survey
@survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:survey_id, :id, :content, answers_attributes: [:id, :question_id, :correct_answer, :content]])
end
end
我的survey.rb模特:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
编辑: 我的 question.rb-模型:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
我的answer.rb-模型
class Answer < ActiveRecord::Base
belongs_to :question
end
我的/surveys/show.html.erb
<td><%= link_to('<< Back to list', {:action => 'index'}, :class => 'action_index') %></td>
<div class="survey show">
<h2><strong>Show survey:</strong></h2>
<table summary="Survey detail view">
<tr>
<th>Survey name: </th>
<td><%= h @survey.name %></td>
</tr>
<tr>
<th>Question: </th>
<td><% for question in @survey.questions do %>
<li><%= h question.content %></li>
<ul>
<% for answer in question.answers do %>
<li><%= h answer.content %></li>
<% end %>
</ul>
<% end %>
</td>
</tr>
<tr>
<th>Created_at: </th>
<td><%= @survey.created_at %></td>
</tr>
<tr>
<td><%= link_to('Edit', {:action => 'edit', :id => @survey.id }, :class => 'action_edit') %></td>
<td><%= link_to('Delete', {:action => 'destroy', :id => @survey.id }, :class => 'action_edit') %></td>
</tr>
</table>
</div>
我的_form_for.html.erb
<%= form_for @survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<% end %>
<%= f.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
我的_question_field.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
我的_answer_field.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.radio_button :correct_answer, true %>
</p>
您已经发布了两次调查模型而不是问题模型。你的问题模型 accept_nested_attributes_for :answers
?
假设您有并且我正确理解了您的结构,您的问题很可能出在您的表单中 - 而不是 f.fields_for :answers
,您应该 ff.fields_for :answers
,嵌套在 f.fields_for :questions
中,因为这是问题而非调查的嵌套资源。所以:
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<%= ff.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<% end %>
我正在根据此 Railscast 在 Rails4 应用程序中尝试嵌套对象。模型调查 has_many :questions,模型依次提问 has_many :answers,模型回答 belongs_to :questions 和 questions belongs_to :survey。
现在,这些模型一开始是完全相互分离的,虽然我不希望这样,但效果很好。我更喜欢将它们嵌套在一起,这样我就可以同时分配和显示不同的对象。
然后我不得不弄清楚如何将这些嵌套 objects/attributes 的强参数列入白名单,并且有很好的问题 here 帮助了我。
当我创建数据库条目时,一切正常。当我想编辑数据库中的对象时,问题就来了。在日志中我得到 "Unpermitted parameter: answers" 即使我已经将每个属性列入白名单,包括用于回答的属性。我就是不明白为什么。
谁能给我指出正确的方向?
我的surveys_controller:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
@surveys = Survey.all
end
def show
@survey = Survey.find(params[:id])
end
def new
@survey = Survey.new
3.times do
question = @survey.questions.build
4.times { question.answers.build }
end
end
def create
@survey = Survey.new(survey_params)
if @survey.save
flash[:notice] = 'Survey was successfully created.'
redirect_to(:action => 'index')
else
render('new')
end
end
def edit
@survey = Survey.find(params[:id])
end
def update
#Find an existing object using form parameters
@survey = Survey.find(params[:id])
#Update the object
if @survey.update_attributes(survey_params)
flash[:notice] = "Survey updated successfully."
#If update succeeds, redirect to 'show' action.
redirect_to(:action => 'show', :id => @survey.id)
else
#Else redisplay the 'edit' form.
render('edit')
end
end
def delete
@survey = Survey.find(params[:id])
end
def destroy
@survey = Survey.find(params[:id]).destroy
flash[:notice] = "Survey destroyed successfully."
redirect_to(:action => 'index')
end
private
def set_survey
@survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:survey_id, :id, :content, answers_attributes: [:id, :question_id, :correct_answer, :content]])
end
end
我的survey.rb模特:
class Survey < ActiveRecord::Base
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
编辑: 我的 question.rb-模型:
class Question < ActiveRecord::Base
belongs_to :survey
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }
scope :sorted, lambda { order("questions.created_at DESC")}
end
我的answer.rb-模型
class Answer < ActiveRecord::Base
belongs_to :question
end
我的/surveys/show.html.erb
<td><%= link_to('<< Back to list', {:action => 'index'}, :class => 'action_index') %></td>
<div class="survey show">
<h2><strong>Show survey:</strong></h2>
<table summary="Survey detail view">
<tr>
<th>Survey name: </th>
<td><%= h @survey.name %></td>
</tr>
<tr>
<th>Question: </th>
<td><% for question in @survey.questions do %>
<li><%= h question.content %></li>
<ul>
<% for answer in question.answers do %>
<li><%= h answer.content %></li>
<% end %>
</ul>
<% end %>
</td>
</tr>
<tr>
<th>Created_at: </th>
<td><%= @survey.created_at %></td>
</tr>
<tr>
<td><%= link_to('Edit', {:action => 'edit', :id => @survey.id }, :class => 'action_edit') %></td>
<td><%= link_to('Delete', {:action => 'destroy', :id => @survey.id }, :class => 'action_edit') %></td>
</tr>
</table>
</div>
我的_form_for.html.erb
<%= form_for @survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<% end %>
<%= f.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
我的_question_field.html.erb
<p>
<%= f.label :content, "Question" %><br />
<%= f.text_area :content, :rows => 3 %><br />
</p>
我的_answer_field.html.erb
<p>
<%= f.label :content, "Answer" %>
<%= f.text_field :content %>
<%= f.radio_button :correct_answer, true %>
</p>
您已经发布了两次调查模型而不是问题模型。你的问题模型 accept_nested_attributes_for :answers
?
假设您有并且我正确理解了您的结构,您的问题很可能出在您的表单中 - 而不是 f.fields_for :answers
,您应该 ff.fields_for :answers
,嵌套在 f.fields_for :questions
中,因为这是问题而非调查的嵌套资源。所以:
<%= f.fields_for :questions do |ff| %>
<%= render 'question_fields', :f => ff %>
<%= ff.fields_for :answers do |fff| %>
<%= render 'answer_fields', :f => fff %>
<% end %>
<% end %>