wake-up-neo.com

Starke Parameter von Rails 4.0 verschachtelten Attribute mit einem Schlüssel, der auf einen Hash zeigt

Ich habe mit Rails 4.x Beta herumgespielt und versucht, verschachtelte Attribute in Verbindung mit carrierwave zu bekommen. Ich bin mir nicht sicher, ob ich die richtige Richtung eingeschlagen habe. Nachdem ich mich umgesehen hatte und schließlich die Rails-Quelle und die starken Parameter angesehen hatte, fand ich die folgenden Hinweise. 

# 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

Also muss man sagen, dass man jedes einzelne Attribut innerhalb des has angeben muss. Ich habe folgendes versucht:

Param's Beispiel:

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

Regler

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

Das Obige funktioniert nicht (es wird keine Trägerwelle ausgelöst), es werden jedoch keine Fehler mehr angezeigt (unzulässige Parameter: Dateiname), wenn die standardmäßig geschachtelten Beispiele verwendet werden, die ich gefunden habe:

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

Wenn jemand helfen könnte, wäre das toll. Ich konnte kein Beispiel mit verschachteltem Schlüssel finden, der auf einen Hash zeigt.

34
John

Meine andere Antwort war meistens falsch - neue Antwort. 

in Ihrem Params-Hash gilt Folgendes: Dateiname ist keinem anderen Hash zugeordnet, sondern einem ActiveDispatch :: Http :: UploadedFile-Objekt. Ihre letzte Codezeile:

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

ist eigentlich korrekt, das Dateiname-Attribut ist jedoch nicht zulässig, da es sich nicht um einen der zulässigen skalaren Typen handelt. Wenn Sie eine Konsole öffnen und ein Params-Objekt in dieser Form initialisieren:

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

und führe es dann gegen deine letzte Zeile aus:

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

Wenn Sie das Gleiche jedoch gegen einen Params-Hash mit der hochgeladenen Datei tun, erhalten Sie 

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

Daher ist es wahrscheinlich eine Fehler- oder Pull-Anforderung an Rails wert. In der Zwischenzeit müssen Sie direkt auf den Parameter filename zugreifen, indem Sie das raw-Objekt params verwenden:

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

Sie haben es also mit has_many-Formularen und starken Parametern zu tun. 

Dies ist der Teil des Params-Hash, auf den es ankommt:

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

wenn Sie so starke Parameter definieren ...

permit(:assets_attributes => [:filename]) 

Die Dinge brechen, weil, wo Rails einen filename erwartet, dieser "0"

Was bedeutet diese Zahl? Es ist der id für das Asset, das Sie über Ihr Formular senden. Nun könnte man zunächst denken, dass man so etwas tun muss 

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

Es sieht so aus, als würde es anderen Syntaxkonventionen mit starken Parametern folgen. Zum Besseren oder zum Schlechten haben sie die Dinge jedoch etwas einfacher gemacht, und alles, was Sie schreiben müssen, ist:

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

Edit - Wie in den Kommentaren auf jpwynn hingewiesen, ist in Rails 4.2.4+ die korrekte Syntax

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

und das sollte funktionieren. 

Wenn Sie mit starken Params an die Wände stoßen, werfen Sie am besten einen Debugger in den Controller und testen die Dinge. params.require(:something).permit(:other_things) ist nur eine Methodenkette, sodass Sie verschiedene Dinge mit dem vollständigen Params-Hash ausprobieren können, bis Sie herausfinden, was funktioniert.

14
Pat McGee

versuchen

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

Ich hatte dieses Problem vor etwa einem Monat und einige Suchanfragen haben diese Lösung ausgegraben. Das Hinzufügen der: id oder: screenshot_id hat das Problem behoben (oder beides, ich kann mich nicht erinnern). Das funktioniert aber in meinem Code. 

6
cgat

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

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

Diese Methode hat Vorteile gegenüber anderen Lösungen. Erlaubt tiefverschachtelte Parameter.

Während andere Lösungen wie: 

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

Nicht.


Quelle:

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

Ich hatte dasselbe Problem, das jetzt behoben wurde. Jetzt müssen Sie nur noch etwas tun 

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

Verwenden Sie pry gem, um zu sehen, welche Art von Attributen Ihr Asset-Objekt sicherstellt, dass es eine ID gibt, und fügen Sie ein anderes fehlendes Attribut hinzu, das dann einwandfrei funktionieren sollte. Die Verwendung von Paperclip-Assets ist mein verschachteltes Objekt in der Fahrzeugklasse und ein Anhang von Bildern wird zum Asset hinzugefügt. Stellen Sie sicher, dass Sie die Validierung im Modell durchführen

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

Durchlaufen Sie in Ihrer Ansicht das Asset, um jedes Bild abzurufen 

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

Wenn das Problem richtig ist, handelt es sich bei asset_attributes um ein Array, bei dem jedes Bild eine Indexspalte und ein Bild hat 

Ihr form_for sollte etwas ähnliches haben, und wenn Sie möchten, können Sie auch eine Vorschau einfügen, damit der Upload seine Bilder mit dem unteren Code anzeigen kann 

<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

Sanitize vor dem Speichern im Controller Sanitize accept_nested_attributes_for Attribute mit 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
0
gilcierweb