Atributos anidados parámetros no permitidos

127

Tengo un Billobjeto, que tiene muchos Dueobjetos. El Dueobjeto también pertenece a a Person. Quiero un formulario que pueda crear el Billy sus elementos secundarios Duesen una sola página. Estoy tratando de crear un formulario utilizando atributos anidados, similares a los de este Railscast .

El código relevante se enumera a continuación:

due.rb

class Due < ActiveRecord::Base
    belongs_to :person
    belongs_to :bill
end

bill.rb

class Bill < ActiveRecord::Base
    has_many :dues, :dependent => :destroy 
    accepts_nested_attributes_for :dues, :allow_destroy => true
end

facturas_controlador.rb

  # GET /bills/new
  def new
      @bill = Bill.new
      3.times { @bill.dues.build }
  end

facturas / _form.html.erb

  <%= form_for(@bill) do |f| %>
    <div class="field">
        <%= f.label :company %><br />
        <%= f.text_field :company %>
    </div>
    <div class="field">
        <%= f.label :month %><br />
        <%= f.text_field :month %>
    </div>
    <div class="field">
        <%= f.label :year %><br />
        <%= f.number_field :year %>
    </div>
    <div class="actions">
        <%= f.submit %>
    </div>
    <%= f.fields_for :dues do |builder| %>
        <%= render 'due_fields', :f => builder %>
    <% end %>
  <% end %>

facturas / _due_fields.html.erb

<div>
    <%= f.label :amount, "Amount" %>        
    <%= f.text_field :amount %>
    <br>
    <%= f.label :person_id, "Renter" %>
    <%= f.text_field :person_id %>
</div>

ACTUALIZAR a bills_controller.rb ¡ Esto funciona!

def bill_params 
  params
  .require(:bill)
  .permit(:company, :month, :year, dues_attributes: [:amount, :person_id]) 
end

Los campos adecuados se representan en la página (aunque Persontodavía no hay un menú desplegable ) y el envío es exitoso. Sin embargo, ninguna de las cuotas secundarias se guarda en la base de datos y se genera un error en el registro del servidor:

Unpermitted parameters: dues_attributes

Justo antes del error, el registro muestra esto:

Started POST "/bills" for 127.0.0.1 at 2013-04-10 00:16:37 -0700
Processing by BillsController#create as HTML<br>
Parameters: {"utf8"=>"✓", 
"authenticity_token"=>"ipxBOLOjx68fwvfmsMG3FecV/q/hPqUHsluBCPN2BeU=",
 "bill"=>{"company"=>"Comcast", "month"=>"April ", 
"year"=>"2013", "dues_attributes"=>{
"0"=>{"amount"=>"30", "person_id"=>"1"}, 
"1"=>{"amount"=>"30", "person_id"=>"2"},
 "2"=>{"amount"=>"30", "person_id"=>"3"}}}, "commit"=>"Create Bill"}

¿Ha habido algún cambio en Rails 4?

jcanipar
fuente
55
Corrección sobre el formato: params.require (: bill) .permit (: empresa,: mes,: año,: dues_attributes => [: cantidad,: person_id])
Andy Copley

Respuestas:

187

Parece que hay un cambio en el manejo de la protección de atributos y ahora debe incluir en la lista blanca los parámetros en el controlador (en lugar de attr_accessible en el modelo) porque la antigua gema opcional strong_parameters se convirtió en parte del Rails Core.

Esto debería verse más o menos así:

class PeopleController < ActionController::Base
  def create
    Person.create(person_params)
  end

private
  def person_params
    params.require(:person).permit(:name, :age)
  end
end

Entonces params.require(:model).permit(:fields)sería usado

y para atributos anidados algo como

params.require(:person).permit(:name, :age, pets_attributes: [:id, :name, :category])

Se pueden encontrar más detalles en los documentos de la API de Ruby edge y strong_parameters en github o aquí

Thorsten Müller
fuente
1
He cambiado mi BillController para que se vea así: def bill_params params.require(:bill).permit(:company, :month, :year, :dues_attributes[:amount, :person_id]) end ahora recibo este error: no hay conversión implícita de Symbol en Integer
jcanipar
2
Bueno, ayuda a colocar el colon en el lugar correcto ... Esto es exactamente lo que hay que hacer. Gracias @ thorsten-muller!
jcanipar
88
¡NO OLVIDES LA ID! pets_attributes: [:id, :name, :category]De lo contrario, cuando edite, cada mascota se creará nuevamente.
Arcolye
8
Debe hacerlo Person.create(person_params)o no llamará al método. En cambio, lo conseguirás ActiveModel::ForbiddenAttributesError.
andorov
16
Además, si desea destruir elementos del formulario, también debe incluir en la lista blanca el :_destroyparámetro oculto . es decirpets_attributes: [:id, :name, :category, :_destroy]
patógeno el
21

De los documentos

To whitelist an entire hash of parameters, the permit! method can be used

params.require(:log_entry).permit!

Los atributos anidados tienen la forma de un hash. En mi aplicación, tengo un modelo Question.rb que acepta atributos anidados para un modelo Answer.rb (donde el usuario crea opciones de respuesta para una pregunta que crea). En el question_controller, hago esto

  def question_params

      params.require(:question).permit!

  end

Se permite todo en el hash de la pregunta, incluidos los atributos de respuesta anidados. Esto también funciona si los atributos anidados tienen forma de matriz.

Dicho esto, me pregunto si hay un problema de seguridad con este enfoque porque básicamente permite todo lo que está dentro del hash sin especificar exactamente qué es, lo que parece contrario al propósito de los parámetros fuertes.

Leahcim
fuente
Impresionante, no puedo permitir un parámetro de rango explícitamente, esto me ahorra algunas horas.
Bartek Skwira
3
Sí, usando .permit! generalmente se ve como una posible preocupación de seguridad. Realmente solo querrás usarlo si el usuario es administrador, pero incluso entonces desconfiaré de su uso.
8bithero
66
Mis atributos anidados también están en una matriz. ¿Es .permit!la única opción? No puedo hacer que funcione incluso con todos los atributos del modelo permitidos porque se ahoga en la matriz.
Clifton Labrum
20

o simplemente puedes usar

def question_params

  params.require(:question).permit(team_ids: [])

end
Amit Agarwal
fuente
12

En realidad, hay una forma de incluir en la lista blanca todos los parámetros anidados.

params.require(:widget).permit(:name, :description).tap do |whitelisted|
  whitelisted[:position] = params[:widget][:position]
  whitelisted[:properties] = params[:widget][:properties]
end

Este método tiene ventaja sobre otras soluciones. Permite permitir parámetros profundamente anidados.

Mientras que otras soluciones como:

params.require(:person).permit(:name, :age, pets_attributes: [:id, :name, :category])

No lo hagas


Fuente:

https://github.com/rails/rails/issues/9454#issuecomment-14167664

nada especial aquí
fuente
3

Hoy me encontré con este mismo problema, mientras trabajaba en los rieles 4, pude hacerlo funcionar estructurando mis campos_por:

<%= f.select :tag_ids, Tag.all.collect {|t| [t.name, t.id]}, {}, :multiple => true %>

Luego, en mi controlador, tengo mis parámetros fuertes como:

private
def post_params
    params.require(:post).permit(:id, :title, :content, :publish, tag_ids: [])
end

Todo funciona!

Kingsley Ijomah
fuente
hola, gracias @KingsleyIjomah, ¿y si quieres incluir en la lista blanca los atributos específicos de los niños?
BKSpurgeon
1

Si usa un campo JSONB, debe convertirlo a JSON con .to_json (ROR)

Wouter Schoofs
fuente