rails modelo formularios crear anidados ruby-on-rails ruby-on-rails-4 nested-attributes nested-form-for

ruby on rails - modelo - Rails 4 NO actualizando atributos anidados



formularios anidados rails (2)

Como @ Philip7899 mencionó como un comentario en la respuesta aceptada, permitir que el usuario establezca la id significa que podrían "robar" los registros secundarios que pertenecen a otro usuario.

Sin embargo , Rails accepts_nested_attributes_for realidad comprueba el id y plantea:

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

Básicamente, las identificaciones se buscan en la asociación de niños (de nuevo, como dijo @glampr). Por lo tanto, el registro hijo que pertenece a otro usuario no se encuentra.

En última instancia, 401 es el estado de respuesta (a diferencia del 404 habitual de ActiveRecord::RecordNotFound )

Sigue algunos códigos que usé para probar el comportamiento.

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

En conclusión , agregar el id a los parámetros permitidos como se indicó anteriormente es correcto y seguro .

Carriles fascinantes.

Problema: en lugar de actualizar los atributos anidados, se crean sobre los atributos anidados existentes cuando #update acción #update del features_controller.rb asociado

Causa probable: creo que el problema radica en mi falta de comprensión en el form_for Rails. Creo que el desglose está en mis vistas, cómo renderizo los atributos anidados persistentes y / o cómo no puedo especificar el ID del atributo anidado, lo que hace que se cree simplemente uno nuevo.

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 (simplificado)

= 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"

Estoy seguro de que hay una mejor manera de lograr el if @feature.new_record? comprobar. También estoy usando algunos enlaces de Javascript para crear formas de atributos anidados dinámicos (que he omitido ), muy influenciados por el Formulario de modelo anidado de Railscast # 196 (revisado)

Me encantaría una implementación realmente agradable de Rails-y para tratar con este tipo de formas anidadas.


Intente agregar :id a la parte :scenario_attributes de su método feature_params . Solo tienes el campo de descripción y la capacidad de permitir una destrucción.

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

Como lo sugirió @vinodadhikary, ya no necesita verificar si la función es un nuevo registro, ya que Rails, específicamente utilizando el método form_for , lo hará por usted.

Actualizar:

No necesita definir if @feature.new_record? ... else if @feature.new_record? ... else en tu forma. Rails lo cuidará cuando uses form_for . Rails verifica si la acción se create o update función de object.persisted? Entonces, puedes actualizar tu formulario a:

= 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"