web-dev-qa-db-ja.com

Rails 4.0強いパラメーターハッシュを指すキーを持つネストされた属性

私はRails 4.xベータ版で遊んでいて、ネストされた属性をCarrierwaveで動作させようとしました。私がやっていることが正しい方向かどうかわかりません。 Rails source and strong parametersで、以下の注意事項を見つけました。

# 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

だから、has内のすべての単一属性をすべて指定する必要があるということで、私は次のことを試しました:

パラメータの例:

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

コントローラ

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

上記は「動作」していません(搬送波をトリガーしていません)が、私が見つけた標準のネストされた例を使用すると、エラー(許可されていないパラメーター:ファイル名)が表示されなくなりました。

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

誰かがそれを助けることができればそれは素晴らしいだろう。ハッシュを指すキーを持つネストされた例を見つけることができませんでした。

34
John

私の他の答えはほとんど間違っていました-新しい答え。

paramsハッシュでは、:filenameは別のハッシュに関連付けられておらず、ActiveDispatch :: Http :: UploadedFileオブジェクトに関連付けられています。最後のコード行:

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

実際には正しいですが、filename属性は許可されていないため、許可されていません scalar types 。コンソールを開いて、この形状のparamsオブジェクトを初期化する場合:

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

そして最後の行に対して実行します:

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

ただし、アップロードされたファイルでparamsハッシュに対して同じことを行うと、

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

そのため、おそらくRailsへのバグまたはプルリクエストの価値があり、その間、生のparamsオブジェクトを使用してファイル名パラメーターに直接アクセスする必要があります。

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

そのため、has_manyフォームと強力なパラメーターを扱っています。

これは重要なparamsハッシュの一部です。

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

このような強力なパラメータを定義すると...

_permit(:assets_attributes => [:filename]) 
_

Railsはfilenameを期待しているので、これは_"0"_

その数はどういう意味ですか?フォームを介して送信するアセットのidです。さて、最初は次のようなことをしなければならないと思うかもしれません

_permit(:assets_attributes => [:id => [:filename]])
_

これは、他の強力なパラメーター構文規則に従っているようです。しかし、良くも悪くも、それらは物事を少し簡単にしました。あなたが書く必要があるのは次のとおりです。

_permit(:assets_attributes => [:asset_id, :filename])
_

Edit- jpwynn がコメントで指摘しているように、Rails 4.2.4+正しい構文は

_permit(:assets_attributes => [:id, :filename])
_

そしてそれは動作するはずです。

強いパラメーターで壁にぶつかったとき、コントローラーでデバッガーを投げてテストするのが最善です。 params.require(:something).permit(:other_things)は単なるメソッドチェーンであるため、動作するものが見つかるまで、完全なparamsハッシュでさまざまなことを試すことができます。

14
Pat McGee

試してみる

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

約1か月前にこの問題が発生しましたが、このソリューションを掘り下げて調べました。問題を修正した:idまたは:screenshot_idを追加していました(または両方とも覚えていません)。これは私のコードでも機能します。

6
cgat

実際、ネストされたすべてのパラメーターをホワイトリストに登録する方法があります。

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

この方法には、他のソリューションよりも利点があります。 これは、深くネストされたパラメータを許可します。

他のソリューションのように:

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

しないでください。


ソース:

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

私は同じ問題を抱えていたので、修正しました。

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

Pry gemを使用して、アセットオブジェクトがどのような属性を持っているかを確認し、IDが存在することを確認し、他の欠落している属性を追加します。 Paperclipアセットを使用しているのは、ビークルクラス内のネストされたオブジェクトであり、画像の添付ファイルがアセットに追加されます。モデルで検証を行うようにしてください

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

ビューでアセットをループして各画像を取得します

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

正しい場合、問題はasset_attributesが配列であり、各画像にインデックス列と画像があることです

Form_forにはこれに似たものが必要です。また、必要に応じてプレビューを含めることもできます。これにより、アップロード用にその下のコードを使用して画像を表示できます

<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

コントローラーで保存する前にサニタイズしますサニタイズは、インデックス付きのaccepts_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