Zagnieżdżone atrybuty niedozwolone parametry

128

Mam Billobiekt, który ma wiele Dueobiektów. DueObiekt również należy do Person. Potrzebuję formularza, który może tworzyć wszystkie elementy Billi jego elementy podrzędne Duesna jednej stronie. Próbuję utworzyć formularz używając zagnieżdżonych atrybutów, podobnych do tych w tym Railscast .

Odpowiedni kod znajduje się poniżej:

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

bills_controller.rb

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

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

bills / _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 bills_controller.rb To działa!

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

Odpowiednie pola są renderowane na stronie (aczkolwiek bez listy rozwijanej Person) i przesyłanie zakończyło się pomyślnie. Jednak żadna ze składek podrzędnych nie jest zapisywana w bazie danych, aw dzienniku serwera jest zgłaszany błąd:

Unpermitted parameters: dues_attributes

Tuż przed błędem dziennik wyświetla następującą informację:

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

Czy zaszły jakieś zmiany w Rails 4?

jcanipar
źródło
5
Poprawka formatowania: params.require (: bill) .permit (: firma,: miesiąc,: rok,: dues_attributes => [: kwota,: person_id])
Andy Copley

Odpowiedzi:

188

Wygląda na to, że nastąpiła zmiana w obsłudze ochrony atrybutów i teraz musisz dodać parametry do białej listy w kontrolerze (zamiast attr_accessible w modelu), ponieważ poprzedni opcjonalny gem strong_parameters stał się częścią Rails Core.

Powinno to wyglądać mniej więcej tak:

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

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

Więc params.require(:model).permit(:fields)zostanie użyty

a dla atrybutów zagnieżdżonych coś w rodzaju

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

Więcej szczegółów można znaleźć w dokumentacji Ruby edge API i strong_parameters na github lub tutaj

thorsten müller
źródło
1
Ja zmieniłem BillController wyglądać tak: def bill_params params.require(:bill).permit(:company, :month, :year, :dues_attributes[:amount, :person_id]) end jestem teraz otrzymuję ten błąd: nie niejawna konwersja symbolu do Integer
jcanipar
2
Cóż, pomaga umieścić okrężnicę we właściwym miejscu… To jest dokładnie to, co trzeba było zrobić. Dzięki @ thorsten-muller!
jcanipar
88
NIE ZAPOMNIJ IDENTYFIKACJI !!!! pets_attributes: [:id, :name, :category]W przeciwnym razie, kiedy edytujesz, każdy zwierzak zostanie utworzony ponownie.
Arcolye
8
Musisz to zrobić Person.create(person_params)lub nie wywoła metody. Zamiast tego dostaniesz ActiveModel::ForbiddenAttributesError.
andorov
16
Ponadto, jeśli chcesz zniszczyć elementy z formularza, musisz również dodać do białej listy ukryty :_destroyparametr. tj.pets_attributes: [:id, :name, :category, :_destroy]
Pathogen
21

Z dokumentów

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

params.require(:log_entry).permit!

Zagnieżdżone atrybuty mają postać skrótu. W mojej aplikacji mam model Question.rb akceptujący zagnieżdżone atrybuty dla modelu Answer.rb (w którym użytkownik tworzy opcje odpowiedzi na tworzone przez siebie pytanie). Robię to w question_controller

  def question_params

      params.require(:question).permit!

  end

Wszystko w hashu pytania jest dozwolone, w tym zagnieżdżone atrybuty odpowiedzi. Działa to również, jeśli zagnieżdżone atrybuty mają postać tablicy.

Powiedziawszy to, zastanawiam się, czy istnieje obawa o bezpieczeństwo w tym podejściu, ponieważ w zasadzie zezwala na wszystko, co znajduje się w hashu bez dokładnego określania, co to jest, co wydaje się sprzeczne z celem silnych parametrów.

Leahcim
źródło
Niesamowite, nie mogę wyraźnie zezwolić na parametr zakresu, oszczędza mi to kilka godzin.
Bartek Skwira
3
Tak, używając .permit! jest zwykle postrzegane jako potencjalny problem dotyczący bezpieczeństwa. Naprawdę chciałbyś go używać tylko wtedy, gdy użytkownik jest administratorem, ale nawet wtedy byłbym ostrożny w jego użyciu.
8bithero,
6
Moje zagnieżdżone atrybuty również znajdują się w tablicy. Czy .permit!to jedyna opcja? Nie mogę zmusić go do pracy nawet z dozwolonymi wszystkimi atrybutami modelu, ponieważ dławi się na tablicy.
Clifton Labrum,
20

lub możesz po prostu użyć

def question_params

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

end
Amit Agarwal
źródło
13

W rzeczywistości istnieje sposób na wyświetlenie białej listy wszystkich zagnieżdżonych parametrów.

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

Ta metoda ma przewagę nad innymi rozwiązaniami. Pozwala zezwolić na głęboko zagnieżdżone parametry.

Podczas gdy inne rozwiązania, takie jak:

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

Nie.


Źródło:

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

nic specjalnego
źródło
3

Dzisiaj natknąłem się na ten sam problem, pracując na szynach 4, udało mi się go uruchomić, tworząc strukturę fields_for jako:

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

Następnie w moim kontrolerze mam swoje mocne parametry, takie jak:

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

Wszystko działa!

Kingsley Ijomah
źródło
cześć dziękuję @KingsleyIjomah - a co jeśli chcesz umieścić na białej liście określone atrybuty dzieci?
BKSpurgeon
1

Jeśli używasz pola JSONB, musisz przekonwertować je na JSON z .to_json (ROR)

Wouter Schoofs
źródło