問題:ネストされた属性を更新する代わりに、作成されています関連付けられた#update
のfeatures_controller.rb
アクションを押したときに、既存のネストされた属性の上に
考えられる原因:問題は、Railsのform_for
に対する理解の欠如にあると思います。内訳は私の見解、永続的なネストされた属性をレンダリングする方法、および/またはネストされた属性の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'] => { <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実装が大好きです。
: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が提案したように、Railsは、特にform_for
メソッドを使用してそれを行うため、機能が新しいレコードであるかどうかを確認する必要がなくなりました。
更新:
フォームでif @feature.new_record? ... else
を定義する必要はありません。 form_for
を使用するとRails)によって処理されます。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"
受け入れられた回答のコメントとして@ Philip7899が述べたように、ユーザーがid
を設定できるようにすることは、別のユーザーに属する子レコードをcould「盗む」ことを意味します。
ただし、Rails accepts_nested_attributes_for
は実際にid
をチェックし、以下を発生させます。
ActiveRecord::RecordNotFound:
Couldn't find Answer with ID=5 for Questionnaire with ID=5
基本的に、IDは子協会で検索されます(@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
を追加することは正しく、safeです。
魅力的なレール。