21

问题:当我点击关联的操作时,它们不是更新嵌套属性,而是在现有嵌套属性之上创建#updatefeatures_controller.rb

可能原因:我认为问题在于我对 Rails 的form_for. 我认为故障在于我的观点,我如何呈现持久的嵌套属性,和/或我如何未能指定嵌套属性的 id,导致它只是创建一个新的

特征.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'] => { <correct object hash> }

    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实现来处理这些嵌套表单。

4

2 回答 2

43

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

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

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

更新:

您无需if @feature.new_record? ... else在表单中定义。Rails 会在您使用form_for. Rails 会检查动作是基于create还是update基于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"
于 2013-09-08T19:23:16.773 回答
4

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

但是,Railsaccepts_nested_attributes_for实际上会检查id 并引发:

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

基本上,在儿童协会中寻找 id(同样,正如@glampr 所说)。因此,没有找到属于另一个用户的子记录。

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

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

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上述允许的参数中添加 是正确且安全的。

迷人的铁轨。

于 2016-01-14T11:55:47.510 回答