Rails 4 NOT updating nested attributes
Try adding :id
to the :scenario_attributes
portion of your feature_params
method. You only have the description field and the ability to allow a destroy.
def feature_params # added => before nested attributes params.require(:feature).permit(:id, :title, :narrative, :price, :eta, scenarios_attributes => [:id, :description, :_destroy])end
As @vinodadhikary suggested, you no longer need to check if feature is a new record, since Rails, specifically using the form_for
method, will do that for you.
Update:
You don't need to define if @feature.new_record? ... else
in your form. It will be taken care by Rails when you use form_for
. Rails checks if the action is going to be create
or update
based on object.persisted?
, so, you can update your form to:
= 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"
As @Philip7899 mentioned as a comment in the accepted answer, allowing the user to set the id
means that they could "steal" children records belonging to another user.
However, Rails accepts_nested_attributes_for
actually checks the id
and raises:
ActiveRecord::RecordNotFound: Couldn't find Answer with ID=5 for Questionnaire with ID=5
Basically the ids are looked for in the children association (again, as said by @glampr). Therefor, the child record belonging to another user is not found.
Ultimately, 401 is the response status (unlike the usual 404 from ActiveRecord::RecordNotFound
)
Follows some code I used to test the behaviour.
let :params do { id: questionnaire.id, questionnaire: { participation_id: participation.id, answers_attributes: answers_attributes } }endlet :evil_params do params.tap do |params| params[:questionnaire][:answers_attributes]['0']['id'] = another_participant_s_answer.id.to_s endendit "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) # passend
In conclusion, adding the id
to the permitted params as stated above is correct and safe.
Fascinating Rails.