Verschachtelte Attribute unzulässige Parameter

127

Ich habe ein BillObjekt, das viele DueObjekte hat. Das DueObjekt gehört auch zu a Person. Ich möchte ein Formular, mit dem das Billund seine untergeordneten DuesElemente auf einer Seite erstellt werden können. Ich versuche, ein Formular mit verschachtelten Attributen zu erstellen, ähnlich denen in diesem Railscast .

Der relevante Code ist unten aufgeführt:

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

bill_controller.rb

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

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

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

UPDATE to bill_controller.rb Das funktioniert!

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

Die richtigen Felder werden auf der Seite gerendert (allerdings noch ohne Dropdown Person) und das Senden ist erfolgreich. Es wird jedoch keine der untergeordneten Gebühren in der Datenbank gespeichert, und im Serverprotokoll wird ein Fehler ausgegeben:

Unpermitted parameters: dues_attributes

Kurz vor dem Fehler zeigt das Protokoll Folgendes an:

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

Hat sich Rails 4 geändert?

jcanipar
quelle
5
Korrektur der Formatierung: params.require (: bill) .permit (: Firma ,: Monat ,: Jahr ,: dues_attributes => [: Betrag ,: person_id])
Andy Copley

Antworten:

187

Anscheinend hat sich die Behandlung des Attributschutzes geändert, und jetzt müssen Sie die Parameter im Controller auf die Whitelist setzen (anstelle von attr_accessible im Modell), da die früheren optionalen gem strong_parameters Teil des Rails Core wurden.

Das sollte ungefähr so ​​aussehen:

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

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

Also params.require(:model).permit(:fields)würde verwendet werden

und für verschachtelte Attribute so etwas wie

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

Weitere Details finden Sie in den Ruby Edge API-Dokumenten und strong_parameters auf github oder hier

thorsten müller
quelle
1
Ich habe meinen BillController so geändert, dass er so aussieht: def bill_params params.require(:bill).permit(:company, :month, :year, :dues_attributes[:amount, :person_id]) end Ich erhalte jetzt den folgenden Fehler: Keine implizite Konvertierung von Symbol in Integer
jcanipar
2
Nun, es hilft, den Doppelpunkt an der richtigen Stelle zu platzieren ... Genau das musste getan werden. Danke @ thorsten-muller!
Jcanipar
88
VERGESSEN SIE NICHT DEN AUSweis !!!! pets_attributes: [:id, :name, :category]Andernfalls wird beim Bearbeiten jedes Haustier erneut erstellt.
Arcolye
8
Sie müssen dies tun, sonst Person.create(person_params)wird die Methode nicht aufgerufen. Stattdessen wirst du bekommen ActiveModel::ForbiddenAttributesError.
Andorov
16
Wenn Sie Elemente aus dem Formular zerstören möchten, müssen Sie auch den ausgeblendeten :_destroyParameter auf die Whitelist setzen . dhpets_attributes: [:id, :name, :category, :_destroy]
Pathogen
21

Aus den Dokumenten

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

params.require(:log_entry).permit!

Verschachtelte Attribute haben die Form eines Hashs. In meiner App akzeptiert ein Question.rb-Modell verschachtelte Attribute für ein Answer.rb-Modell (wobei der Benutzer Antwortoptionen für eine von ihm erstellte Frage erstellt). Im Fragencontroller mache ich das

  def question_params

      params.require(:question).permit!

  end

Alles im Fragen-Hash ist zulässig, einschließlich der verschachtelten Antwortattribute. Dies funktioniert auch, wenn die verschachtelten Attribute die Form eines Arrays haben.

Trotzdem frage ich mich, ob dieser Ansatz ein Sicherheitsrisiko darstellt, da er grundsätzlich alles zulässt, was sich im Hash befindet, ohne genau anzugeben, was es ist, was dem Zweck starker Parameter zu widersprechen scheint.

Leahcim
quelle
Genial, ich kann einen Bereichsparameter nicht explizit zulassen, das spart mir einige Stunden.
Bartek Skwira
3
Ja, mit .permit! wird normalerweise als potenzielles Sicherheitsrisiko angesehen. Sie möchten es nur dann wirklich verwenden, wenn der Benutzer ein Administrator ist, aber selbst dann wäre ich vorsichtig mit seiner Verwendung.
8bithero
6
Meine verschachtelten Attribute befinden sich ebenfalls in einem Array. Ist .permit!die einzige Option? Ich kann es nicht zum Laufen bringen, selbst wenn alle Attribute des Modells zulässig sind, da es das Array drosselt.
Clifton Labrum
20

oder Sie können einfach verwenden

def question_params

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

end
Amit Agarwal
quelle
12

Tatsächlich gibt es eine Möglichkeit, alle verschachtelten Parameter auf die weiße Liste zu setzen.

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

Diese Methode hat gegenüber anderen Lösungen Vorteile. Es erlaubt, tief verschachtelte Parameter zuzulassen.

Während andere Lösungen wie:

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

Tu es nicht.


Quelle:

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

nichts Besonderes hier
quelle
3

Heute bin ich auf dasselbe Problem gestoßen, als ich an Schienen 4 gearbeitet habe. Ich konnte es zum Laufen bringen, indem ich meine Felder wie folgt strukturierte:

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

Dann habe ich in meinem Controller meine starken Parameter wie folgt:

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

Alles funktioniert!

Kingsley Ijomah
quelle
Hallo, danke @KingsleyIjomah - was ist, wenn Sie bestimmte Attribute der Kinder auf die weiße Liste setzen möchten?
BKSpurgeon
1

Wenn Sie ein JSONB-Feld verwenden, müssen Sie es mit .to_json (ROR) in JSON konvertieren.

Wouter Schoofs
quelle