Rails 4不更新嵌套属性

问题:当我点击相关features_controller.rb#update操作时,它们是在现有嵌套属性的基础上创建的 ,而不是更新嵌套属性。

可能的原因:我认为问题在于我在Rails的form_for缺乏理解。 我认为故障在我的视图中,我如何呈现持久的嵌套属性,和/或我如何指定嵌套属性的id失败,导致它只是创建一个新的id

feature.rb

 class Feature < ActiveRecord::Base ... has_many :scenarios accepts_nested_attributes_for :scenarios, allow_destroy: true, reject_if: :all_blank ... end 

features_controller.rb

 def update ... project = Project.find(params[:project_id]) @feature = Feature.find(params[:id]) if @feature.update_attributes(feature_params) # checking feature_params looks good... # feature_params['scenarios'] => {  } redirect_to project else render :edit end end ... private def feature_params params.require(:feature).permit(:title, :narrative, :price, :eta, scenarios_attributes[:description, :_destroy]) end 

_form.html.haml(简体)

 = form_for [@project, @feature] do |f| ... - if @feature.new_record? -# if we are creating new feature = f.fields_for :scenarios, @feature.scenarios.build do |builder| = builder.label :description, "Scenario" = builder.text_area :description, rows: "3", autocomplete: "off" - else -# if we are editing an existing feature = f.fields_for :scenarios do |builder| = builder.label :description, "Scenario" = builder.text_area :description, rows: "3", autocomplete: "off" 

我确定有更好的方法来实现if @feature.new_record? 校验。 我也使用一些Javascript钩子来创建动态嵌套属性表单(我已经遗漏了),受Railscast#196嵌套模型表(修订版)的影响很大

我会喜欢一个非常好的Rails-y实现处理这些嵌套的表单。

尝试将:id添加到feature_params方法的:scenario_attributes部分。 您只有描述字段和允许销毁的能力。

 def feature_params # added => before nested attributes params.require(:feature).permit(:id, :title, :narrative, :price, :eta, scenarios_attributes => [:id, :description, :_destroy]) end 

正如@vinodadhikary建议的那样,您不再需要检查function是否是新记录,因为Rails(特别是使用form_for方法)将为您执行此操作。

更新:

您不需要定义if @feature.new_record? ... else if @feature.new_record? ... else在你的表格中。 当您使用form_for时,Rails会照顾它。 Rails会根据object.persisted?检查是否要createupdate object.persisted? ,所以,您可以将表单更新为:

 = form_for [@project, @feature] do |f| ... = f.fields_for :scenarios, @feature.scenarios.build do |builder| = builder.label :description, "Scenario" = builder.text_area :description, rows: "3", autocomplete: "off" 

正如@ Philip7899在接受的答案中提到的那样,允许用户设置id意味着他们可以 “窃取”属于另一个用户的子记录。

但是 ,Rails accepts_nested_attributes_for实际检查id并引发:

 ActiveRecord::RecordNotFound: Couldn't find Answer with ID=5 for Questionnaire with ID=5 

基本上,在儿童协会中寻找ids(再次,如@glampr所述)。 因此,找不到属于另一个用户的子记录。

最终, 401是响应状态(与ActiveRecord::RecordNotFound通常的404不同)

遵循我用来测试行为的一些代码。

 let :params do { id: questionnaire.id, questionnaire: { participation_id: participation.id, answers_attributes: answers_attributes } } end let :evil_params do params.tap do |params| params[:questionnaire][:answers_attributes]['0']['id'] = another_participant_s_answer.id.to_s end end it "doesn't mess with other people's answers" do old_value = another_participant_s_answer.value put :update, evil_params expect(another_participant_s_answer.reload.value).to eq(old_value) # pass expect(response.status).to eq(401) # pass end 

总之 ,如上所述将id添加到允许的参数中是正确和安全的

迷人的Rails。