Rails 4.0 Strong参数使用指向散列的键来嵌套属性

我正在玩Rails 4.xtesting版,并尝试使用carrierwave来获取嵌套属性。 不知道我在做什么是正确的方向。 经过四周search,然后最终看到了轨道来源和强大的参数,我发现下面的笔记。

# 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

所以它说你必须指定每一个单一的属性,我试着以下几点:

帕拉姆的例子:

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

如果有人能帮上忙,那就太好了。 我无法find与指向散列的键嵌套的示例。

我的其他答案大多是错误的 – 新的答案。

在你的params散列中,:filename不与另一个散列关联,它与一个ActiveDispatch :: Http :: UploadedFile对象相关联。 你的最后一行代码:

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

实际上是正确的,但是文件名属性不被允许,因为它不是允许的标量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对象直接访问filename参数:

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

所以,你正在处理has_manyforms和强大的参数。

这是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]) 

事情打破,因为哪里的铁轨期望一个filename就得到这个"0"

这个数字是什么意思? 这是您通过表单提交的资产的id 。 现在最初你可能会认为你必须做类似的事情

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

这看起来像遵循其他强大的参数语法约定。 然而,无论好坏,他们已经使事情变得更容易一些,你所要写的是:

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

编辑 – 正如jpwynn在评论中指出的,在Rails 4.2.4 +中,正确的语法是

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

这应该工作。

当你用强参数打墙时,最好的办法是在你的控制器中放一个debugging器,然后testing一下。 params.require(:something).permit(:other_things)只是一个方法链,所以你可以在完整的参数哈希上尝试不同的东西,直到find有效的东西。

尝试

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

大约一个月前我曾经遇到过这个问题,有些人在search这个解决scheme。 这是添加:ID或:screenshot_id修复问题(或两者,我不记得)。 这在我的代码虽然工作。

实际上有一种方法可以将所有嵌套的参数白名单。

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

这种方法比其他解决scheme有优势。 它允许允许深嵌套的参数。

而其他解决scheme如:

 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]). 

使用撬gem,看看你的资产对象有什么样的属性确保一个ID和添加其他缺less的属性,那应该是完美的。 我使用回形针资产是车辆类中的嵌套对象,图像附件被添加到资产中。 确保你在模型中进行validation

 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> 

在保存在控制器中之前进行清理对​​具有索引的属性进行sanitize accep_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