Rails 4.0 Strong Parameters ha nidificato gli attributi con una chiave che punta ad un hash

Stavo giocando con Rails 4.x beta e cercando di ottenere attributi nidificati che funzionassero con carrierwave. Non sono sicuro se quello che sto facendo è la giusta direzione. Dopo aver cercato in giro, e poi alla fine guardando l’origine delle rotaie e i forti parametri, ho trovato le note seguenti.

# 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

Quindi, dicendo che devi specificare ogni singolo attributo all’interno di has, ho provato quanto segue:

Esempio di parametro:

 {"utf8"=>"✓", "authenticity_token"=>"Tm54+v9DYdBtWJ7qPERWzdEBkWnDQfuAQrfT9UE8VD=", "screenshot"=>{ "title"=>"afs", "assets_attributes"=>{ "0"=>{ "filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40 @tempfile=#, @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"} 

controllore

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

Quanto sopra non sta “funzionando” (non sta triggersndo carrierwave), ma non ottengo più errori (Parametri non ammessi: nomefile) quando si usano gli esempi nidificati standard che ho trovato ex:

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

Se qualcuno potesse aiutarlo sarebbe fantastico. Non ero in grado di trovare un esempio con nidificato con una chiave che punta a un hash.

La mia altra risposta era per lo più sbagliata – nuova risposta.

nel tuo hash params,: filename non è associato ad un altro hash, è associato ad un object ActiveDispatch :: Http :: UploadedFile. La tua ultima riga di codice:

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

è in realtà corretto, tuttavia, l’attributo filename non è consentito poiché non è uno dei tipi scalari consentiti. Se apri una console e inizializzi un object params in questa forma:

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

e quindi eseguirlo contro la tua ultima riga:

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

Tuttavia, se si fa lo stesso con un hash params con il file caricato, si ottiene

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

Quindi, probabilmente vale un bug o una richiesta di pull a Rails, e nel frattempo, dovrai accedere direttamente al parametro filename usando l’object params raw:

 params[:screenshot][:assets_attributes]["0"][:filename] 

Quindi, hai a che fare con has_many form e parametri forti.

Questa è la parte dell’hash params che conta:

 "assets_attributes"=>{ "0"=>{ "filename"=>#, @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"> } } 

quando definisci parametri forti come questo …

 permit(:assets_attributes => [:filename]) 

Le cose si rompono, perché dove rails si aspetta un filename sta ottenendo questo "0"

Cosa significa quel numero? È l’ id per la risorsa che invii tramite il modulo. Ora inizialmente potresti pensare di dover fare qualcosa del genere

 permit(:assets_attributes => [:id => [:filename]]) 

Sembra che segua altre convenzioni sulla syntax dei forti parametri. Tuttavia, nel bene o nel male, hanno reso le cose un po ‘più semplici e tutto ciò che devi scrivere è:

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

Modifica – Come sottolineato da jpwynn nei commenti, in Rails 4.2.4+ la syntax corretta è

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

e dovrebbe funzionare.

Quando colpisci muri con parametri forti, la cosa migliore da fare è lanciare un debugger nel tuo controller e testare le cose. params.require(:something).permit(:other_things) è solo una catena di metodi in modo che tu possa provare diverse cose params.require(:something).permit(:other_things) params completo finché non trovi ciò che funziona.

provare

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

Ho avuto questo problema circa un mese fa e alcune ricerche hanno scavato questa soluzione. Stava aggiungendo: id o: screenshot_id che risolveva il problema (o entrambi, non ricordo). Questo funziona nel mio codice però.

In realtà esiste un modo per elencare semplicemente tutti i parametri nidificati.

 params.require(:screenshot).permit(:title).tap do |whitelisted| whitelisted[:assets_attributes ] = params[:screenshot][:assets_attributes ] end 

Questo metodo ha vantaggio rispetto ad altre soluzioni. Permette di permettere i parametri deep-nested.

Mentre altre soluzioni come:

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

Non farlo.


Fonte:

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

Ho avuto lo stesso problema, ma ora ho risolto tutto ciò che devi fare

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

Usa pry gem per vedere che tipo di attributi ha il tuo object asset, assicurandoti un id e aggiungendo altri attributi mancanti, che dovrebbero quindi funzionare perfettamente. Sto usando le risorse di paperclip è il mio object annidato all’interno della class del veicolo e un allegato di immagini viene aggiunto alla risorsa. assicurati di fare la validazione nel modello

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

Nella tua vista, passa attraverso la risorsa per ottenere ogni immagine

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

Se sono corretto il tuo problema è che asset_attributes è una matrice con ogni immagine che ha una colonna di indice e un’immagine

Il tuo form_for dovrebbe avere qualcosa di simile a questo e se vuoi puoi anche includere l’anteprima in modo che il caricamento possa visualizzare le loro immagini usando il codice di fondo per quello

 

Vehicle Image Upload

<%= f.fields_for :assets do |asset_fields| %> <% if asset_fields.object.new_record? %>

<%= asset_fields.file_field :image %>

<% end %> <% end %>

Vehicle Image

<%= 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 %>

Disinfetta prima di salvare nel controllore Sanitize accept_nested_attributes_for attributi con index.

 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