web-dev-qa-db-fra.com

Rails 4.0 Strong Attributs imbriqués de paramètres avec une clé qui pointe vers un hachage

Je jouais avec la version bêta de Rails 4.x et essayais de faire fonctionner les attributs imbriqués avec carrierwave. Je ne sais pas si ce que je fais est la bonne direction. Après avoir cherché autour de nous, puis finalement examiné la source Rails et les paramètres importants, j'ai trouvé les notes ci-dessous. 

# 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

Donc, il faut dire que vous devez spécifier chaque attribut au sein du has, j'ai essayé ce qui suit:

Exemple de Param:

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

Manette

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

Ce qui précède ne fonctionne pas (il ne déclenche pas carrierwave), mais je ne reçois plus d'erreurs (paramètres non autorisés: nom de fichier) lorsque j'utilise les exemples imbriqués standard que j'ai trouvés, par exemple:

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

Si quelqu'un pouvait aider, ce serait génial. Je n'ai pas été en mesure de trouver un exemple avec imbriqué avec une clé qui pointe vers un hachage.

34
John

Mon autre réponse était la plupart du temps erronée - nouvelle réponse. 

dans votre hachage params,: nomfichier n'est pas associé à un autre hachage, il est associé à un objet ActiveDispatch :: Http :: UploadedFile. Votre dernière ligne de code:

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

est en fait correct, cependant, l’attribut filename n’est pas autorisé car il ne s’agit pas d’un des types autorisés scalar . Si vous ouvrez une console et initialisez un objet params sous cette forme:

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

puis lancez-le contre votre dernière ligne:

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

Cependant, si vous faites la même chose avec un hachage params avec le fichier téléchargé, vous obtenez 

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

Donc, il vaut probablement la peine de demander un bogue ou une requête d'extraction à Rails, et entre temps, vous devrez accéder directement au paramètre filename à l'aide de l'objet raw params:

params[:screenshot][:assets_attributes]["0"][:filename]
33
Jim Deville

Donc, vous avez affaire à de nombreuses formes has_ ​​et de nombreux paramètres. 

C'est la partie du hachage params qui compte:

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

quand vous définissez des paramètres forts comme celui-ci ...

permit(:assets_attributes => [:filename]) 

Les choses se cassent, parce que quand Rails s'attend à une filename, il obtient ce "0"

Qu'est-ce que ce nombre signifie? Il s'agit de la id de l'actif que vous soumettez via votre formulaire. Maintenant, au début, vous pourriez penser que vous devez faire quelque chose comme 

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

Cela semble suivre les autres conventions de syntaxe de paramètres forts. Cependant, pour le meilleur et pour le pire, ils ont rendu les choses un peu plus faciles, et tout ce que vous avez à écrire est:

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

Edit - Comme jpwynn a été souligné dans les commentaires, dans Rails 4.2.4+, la syntaxe correcte est

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

et cela devrait fonctionner. 

Lorsque vous frappez des murs avec des paramètres puissants, la meilleure chose à faire est de lancer un débogueur dans votre contrôleur et de tester les choses. params.require(:something).permit(:other_things) est juste une chaîne de méthodes vous permettant d’essayer différentes choses sur le hash complet de params jusqu’à ce que vous trouviez ce qui fonctionne.

14
Pat McGee

essayer

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

J'ai eu ce problème il y a environ un mois et certaines recherches ont permis de découvrir cette solution. L'ajout de: id ou screenshot_id a résolu le problème (ou les deux, je ne me souviens plus). Cela fonctionne dans mon code cependant. 

6
cgat

En fait, il existe un moyen de simplement mettre en liste blanche tous les paramètres imbriqués.

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

Cette méthode présente un avantage par rapport aux autres solutions. Il permet d'autoriser des paramètres imbriqués en profondeur.

Alors que d'autres solutions comme: 

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

Ne pas.


La source:

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

4

J'ai eu le même problème vient de le réparer maintenant tout ce que vous avez à faire est 

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

Utilisez pry gem pour voir le type d'attribut que votre objet d'actif a pour vous assurer qu'il possède un identifiant et ajoute un autre attribut manquant, qui devrait alors fonctionner parfaitement. est ajouté à la ressource ..__, assurez-vous de valider le modèle

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

Dans votre vue, parcourez l'actif pour obtenir chaque image 

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

Si vous avez raison, votre problème est que asset_attributes est un tableau avec chaque image ayant une colonne d’index et une image. 

Votre form_for devrait avoir quelque chose de similaire à celui-ci et si vous le souhaitez, vous pouvez également inclure un aperçu afin que le téléchargement puisse afficher leurs images, utilisez le code du bas pour cela. 

<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
Tunde Adetula

Désinfectez avant d'enregistrer dans le contrôleur Désinfectez les attributs avec index avec les attributs accept_nested_attributes_for.

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
gilcierweb