Rails 4.0 Strong Parameters zagnieżdżone atrybuty z kluczem wskazującym na hash

Bawiłem się Rails 4.X beta i próbuje uzyskać zagnieżdżone atrybuty współpracujące z carrierwave. Nie jestem pewien, czy to, co robię, jest dobrym kierunkiem. Po przeszukaniu wokół, a następnie w końcu patrząc na źródło rails i silne parametry znalazłem poniższe notatki.

# Note that if you use +permit+ in a key that points to a hash,
# it won't allow all the hash. You also need to specify which
# attributes inside the hash should be whitelisted.

Https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb

Więc mówi, że musisz określić każdy pojedynczy atrybut w has, próbowałem następujących:

Przykład Parama:

{"utf8"=>"✓",
 "authenticity_token"=>"Tm54+v9DYdBtWJ7qPERWzdEBkWnDQfuAQrfT9UE8VD=",
 "screenshot"=>{
   "title"=>"afs",
   "assets_attributes"=>{
     "0"=>{
       "filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
                      @tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
                      @original_filename="EK000005.JPG",
                      @content_type="image/jpeg",
                      @headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
     }
   }
 },
 "commit"=>"Create Screenshot"}

Controller

def screenshot_params
  params.require(:screenshot).permit(:title,
    :assets_attributes => [:filename => [:@tempfile,:@original_filename,:@content_type,:@headers] 

Powyższe nie działa (nie uruchamia carrierwave), jednak nie dostaję już błędów (nieprzepisowe parametry: nazwa pliku) przy użyciu standardowych zagnieżdżonych przykładów, które znalazłem np:

def screenshot_params
  params.require(:screenshot).permit(:title, assets_attributes: :filename)
Gdyby ktoś mógł pomóc, byłoby świetnie. Nie udało mi się znaleźć przykładu z zagnieżdżonym kluczem, który wskazuje na hash.
Author: Jim Deville, 2013-01-23

6 answers

Moja druga odpowiedź była w większości błędna-Nowa odpowiedź.

W Twoim parametrze hash: filename nie jest skojarzony z innym Hashem, jest powiązany z obiektem Activedispatch::Http:: UploadedFile. Ostatnia linia kodu:

def screenshot_params
  params.require(:screenshot).permit(:title, assets_attributes: :filename)

Jest poprawne, jednak atrybut filename nie jest dozwolony, ponieważ nie jest jednym z dozwolonych typów skalarnych . Jeśli otworzysz konsolę i zainicjalizujesz obiekt params w tym kształcie:

params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: 'a string'}}}

A następnie uruchom go przeciwko swojej ostatni wiersz:

p = params.require(:screenshot).permit(:title, assets_attributes: :filename)
# => {"title" => "afa", "assets_attributes"=>{"0"=>{"filename"=>"abc"}}}

Jeśli jednak zrobisz to samo z hashiem params z przesłanym plikiem, otrzymasz

upload = ActionDispatch::Http::UplaodedFile.new tempfile: StringIO.new("abc"), filename: "abc"
params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: upload}}}
p = params.require(:screenshot).permit(:title, assets_attributes: :filename)

# => {"title" => "afa", "assets_attributes"=>{"0"=>{}}}

Więc, prawdopodobnie warto zrobić bug lub pull request Do Rails, a w międzyczasie, będziesz musiał bezpośrednio uzyskać dostęp do parametru filename używając raw params object:

params[:screenshot][:assets_attributes]["0"][:filename]
 33
Author: Jim Deville,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-23 18:31:51

Więc masz do czynienia z has_many formularzy i silnych parametrów.

To jest ta część params hash, która się liczy:

"assets_attributes"=>{
    "0"=>{
          "filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
                  @tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
                  @original_filename="EK000005.JPG",
                  @content_type="image/jpeg",
                  @headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
 }
}

Kiedy zdefiniujesz takie silne parametry...

permit(:assets_attributes => [:filename]) 

Rzeczy się psują, bo tam, gdzie rails oczekuje filename to dostaje to "0"

Co oznacza ta liczba? Jest to id dla aktywów, które przesyłasz za pośrednictwem formularza. Teraz początkowo możesz pomyśleć, że musisz zrobić coś w stylu
permit(:assets_attributes => [:id => [:filename]])

To wygląda tak podąża za innymi silnymi konwencjami składni parametrów. Jednak, na dobre i na złe, sprawili, że wszystko było trochę łatwiejsze, a wszystko, co musisz napisać, to:

permit(:assets_attributes => [:asset_id, :filename])

Edit - Jak jpwynn zauważył w komentarzach, w Rails 4.2.4 + poprawna składnia to

permit(:assets_attributes => [:id, :filename])
I to powinno zadziałać.

Kiedy uderzasz w ściany silnymi paramami, najlepszą rzeczą do zrobienia jest wrzucenie debuggera do kontrolera i przetestowanie wszystkiego. params.require(:something).permit(:other_things) jest tylko łańcuchem metod, więc można Wypróbuj różne rzeczy na pełnym hash params, aż znajdziesz to, co działa.

 14
Author: Pat McGee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:38

Try

def screenshot_params
  params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
end

Miałem ten problem około miesiąc temu i niektóre poszukiwania wokół wykopali To rozwiązanie. To było dodanie :id lub: screenshot_id, które naprawiło problem(lub obu, nie pamiętam). To działa w moim kodzie.

 6
Author: cgat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-17 09:09:32

W rzeczywistości istnieje sposób na białą listę wszystkich zagnieżdżonych parametrów.

params.require(:screenshot).permit(:title).tap do |whitelisted|
  whitelisted[:assets_attributes ] = params[:screenshot][:assets_attributes ]
end
Metoda ta ma przewagę nad innymi rozwiązaniami. pozwala zezwolić na głębokie zagnieżdżanie parametrów.

Podczas gdy inne rozwiązania jak:

params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
Nie.]}

Źródło:

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

 4
Author: nothing-special-here,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-25 21:00:03

Miałem ten sam problem tylko go naprawiłem teraz wszystko co musisz zrobić to

params.require(:vehicle).permit(:user_id, assets_attributes: [:id, :image]).

Użyj pry gem, aby zobaczyć, jakiego rodzaju atrybuty posiada obiekt zasobu, aby upewnić się, że istnieje identyfikator i dodać inny brakujący atrybut, który powinien działać idealnie. Używam zasobów paperclip to mój zagnieżdżony obiekt wewnątrz klasy pojazdu, a załącznik obrazów jest dodawany do zasobu. upewnij się, że wykonasz walidację w modelu

accepts_nested_attributes_for :assets, allow_destroy: true
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/

W pętli widoku przez zasób, aby uzyskać każdy obraz

<%= @vehicle.assets.size %>
    <% for asset in @vehicle.assets %>
        <%=link_to image_tag (asset.image.url(:thumb)) %>
    <% end %>

Jeśli poprawię twój problem polega na tym, że asset_attributes jest tablicą z każdym obrazem mającym kolumnę indeksu i obraz

Twój formula_for powinien mieć coś podobnego do tego, a jeśli chcesz, możesz również dołączyć Podgląd, aby upload mógł wyświetlać swoje obrazy, użyj dolnego kodu do tego

<div class="field">
    <h3>Vehicle Image Upload</h3>
    <%= f.fields_for :assets do |asset_fields| %>

        <% if asset_fields.object.new_record? %>
            <p>
                <%= asset_fields.file_field :image %>
            </p>
        <% end %>
    <% end %>
</div>

<div class="field">
    <h4>Vehicle Image</h4>
    <%= f.fields_for :assets do |asset_fields| %>

        <% unless asset_fields.object.new_record? %>
          <%= link_to image_tag(asset_fields.object.image.url(:thumb)),
                    asset_fields.object.image.url(:original)%>
          <%= asset_fields.check_box :_destroy %>
        <% end %>
    <% end %>
</div>
 0
Author: Tunde Adetula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-04 17:50:22

Sanitize przed zapisaniem w kontrolerze Sanitize accepts_nested_attributes_for atrybuty z indeksem.

before_action :sanitize_fields_params, :only => [:create, :update]

def sanitize_fields_params

    product_free_shippings_attributes = params[:product][:product_free_shippings_attributes]

    product_free_shippings_attributes.each do |index, key_value|
      params[:product][:product_free_shippings_attributes]["#{index}"][:weight] = clear_decimal(key_value[:weight])
      params[:product][:product_free_shippings_attributes]["#{index}"][:height] = clear_decimal(key_value[:height])
      params[:product][:product_free_shippings_attributes]["#{index}"][:width] = clear_decimal(key_value[:width])
      params[:product][:product_free_shippings_attributes]["#{index}"][:depth] = clear_decimal(key_value[:depth])
    end
 end

 def clear_decimal(field) 
    return (field.to_s.gsub(/[^\d]/, '').to_d / 100.to_d) unless field.blank?
  end
 0
Author: gilcierweb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-02 14:03:23