Attributs imbriqués paramètres non autorisés

127

J'ai un Billobjet, qui a de nombreux Dueobjets. L' Dueobjet appartient également à un Person. Je veux un formulaire qui puisse créer le Billet ses enfants Duessur une seule page. J'essaie de créer un formulaire en utilisant des attributs imbriqués, similaires à ceux de ce Railscast .

Le code pertinent est répertorié ci-dessous:

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

factures_controller.rb

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

factures / _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 %>

factures / _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>

MISE À JOUR vers factures_controller.rb Cela fonctionne!

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

Les champs appropriés sont rendus sur la page (mais sans liste déroulante pour le Personmoment) et la soumission est réussie. Cependant, aucune des cotisations enfants n'est enregistrée dans la base de données et une erreur est renvoyée dans le journal du serveur:

Unpermitted parameters: dues_attributes

Juste avant l'erreur, le journal affiche ceci:

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

Y a-t-il eu des changements dans Rails 4?

jcanipar
la source
5
Correction du formatage: params.require (: bill) .permit (: company,: month,: year,: cotisations_attributes => [: amount,: person_id])
Andy Copley

Réponses:

187

Il semble qu'il y ait un changement dans la gestion de la protection des attributs et que vous devez maintenant ajouter les paramètres à la liste blanche dans le contrôleur (au lieu de attr_accessible dans le modèle) car l'ancien gem optionnel strong_parameters est devenu une partie du noyau Rails.

Cela devrait ressembler à ceci:

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

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

Donc params.require(:model).permit(:fields)serait utilisé

et pour les attributs imbriqués quelque chose comme

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

Vous trouverez plus de détails dans la documentation de l'API Ruby edge et dans strong_parameters sur github ou ici

Thorsten Müller
la source
1
J'ai changé mon BillController pour qu'il ressemble à ceci: J'obtiens def bill_params params.require(:bill).permit(:company, :month, :year, :dues_attributes[:amount, :person_id]) end maintenant cette erreur: pas de conversion implicite de Symbol en Integer
jcanipar
2
Eh bien, cela aide à mettre le côlon au bon endroit ... C'est exactement ce qu'il fallait faire. Merci @ thorsten-muller!
jcanipar
88
N'OUBLIEZ PAS L'ID !!!! pets_attributes: [:id, :name, :category]Sinon, lorsque vous modifiez, chaque animal sera de nouveau créé.
Arcolye
8
Vous devez faire Person.create(person_params)ou il n'appellera pas la méthode. Au lieu de cela, vous obtiendrez ActiveModel::ForbiddenAttributesError.
andorov
16
De plus, si vous souhaitez détruire des éléments du formulaire, vous devez également ajouter le :_destroyparamètre masqué à la liste blanche . iepets_attributes: [:id, :name, :category, :_destroy]
Pathogen
21

À partir des documents

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

params.require(:log_entry).permit!

Les attributs imbriqués se présentent sous la forme d'un hachage. Dans mon application, j'ai un modèle Question.rb qui accepte les attributs imbriqués pour un modèle Answer.rb (où l'utilisateur crée des choix de réponse pour une question qu'il crée). Dans le questions_controller, je fais ceci

  def question_params

      params.require(:question).permit!

  end

Tout dans le hachage de question est autorisé, y compris les attributs de réponse imbriqués. Cela fonctionne également si les attributs imbriqués se présentent sous la forme d'un tableau.

Cela dit, je me demande s'il y a un problème de sécurité avec cette approche, car elle autorise fondamentalement tout ce qui se trouve à l'intérieur du hachage sans spécifier exactement ce que c'est, ce qui semble contraire à l'objectif des paramètres forts.

Leahcim
la source
Génial, je ne peux pas autoriser explicitement un paramètre de plage, cela me fait gagner quelques heures.
Bartek Skwira
3
Ouais, en utilisant .permit! est généralement considérée comme un problème de sécurité potentiel. Vous ne voudriez vraiment l'utiliser que si l'utilisateur est un administrateur, mais même dans ce cas, je me méfierais de son utilisation.
8bithero
6
Mes attributs imbriqués sont également dans un tableau. Est-ce .permit!la seule option? Je ne peux pas le faire fonctionner même avec tous les attributs du modèle autorisés car il s'étouffe sur le tableau.
Clifton Labrum
20

ou vous pouvez simplement utiliser

def question_params

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

end
Amit Agarwal
la source
12

En fait, il existe un moyen de simplement lister tous les paramètres imbriqués.

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

Cette méthode a un avantage par rapport aux autres solutions. Il permet d'autoriser des paramètres profondément imbriqués.

Alors que d'autres solutions comme:

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

Ne fais pas ça.


La source:

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

rien de spécial ici
la source
3

Aujourd'hui je suis tombé sur ce même problème, alors que je travaillais sur rails 4, j'ai pu le faire fonctionner en structurant mes champs comme:

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

Ensuite, dans mon contrôleur, j'ai mes paramètres forts comme:

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

Tout fonctionne!

Kingsley Ijomah
la source
salut merci @KingsleyIjomah - et si vous voulez lister les attributs spécifiques des enfants?
BKSpurgeon
1

Si vous utilisez un champ JSONB, vous devez le convertir en JSON avec .to_json (ROR)

Wouter Schoofs
la source