35

我在玩 Rails 4.x beta 并试图让嵌套属性与carrierwave一起使用。不知道我在做什么是正确的方向。在四处搜索之后,最终查看了 rails 源和强大的参数,我找到了以下注释。

# 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)

如果有人可以提供帮助,那就太好了。我找不到一个嵌套了指向哈希的键的示例。

4

6 回答 6

34

我的另一个答案大多是错误的 - 新答案。

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

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

实际上是正确的,但是,不允许使用 filename 属性,因为它不是允许的标量类型之一。如果你打开一个控制台,并以这个形状初始化一个 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]
于 2013-01-23T18:31:51.093 回答
14

因此,您正在处理 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])

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

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

这应该有效。

当您使用强大的参数碰壁时,最好的办法是在您的控制器中添加一个调试器并进行测试。params.require(:something).permit(:other_things)只是一个方法链,因此您可以在完整的参数哈希上尝试不同的东西,直到找到有效的方法。

于 2014-01-13T03:31:01.050 回答
6

尝试

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

大约一个月前我遇到了这个问题,一些搜索发现了这个解决方案。它正在添加解决问题的 :id 或 :screenshot_id (或两者,我不记得了)。不过,这在我的代码中有效。

于 2013-10-17T09:09:32.783 回答
4

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

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

于 2014-09-25T21:00:03.143 回答
0

我有同样的问题,现在解决了,你要做的就是

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

使用 pry gem 查看您的资产对象具有什么样的属性,确保有一个 id 并添加其他缺少的属性,然后应该可以正常工作。我正在使用回形针资产是我在车辆类中的嵌套对象,并且图像附件被添加到资产中。确保在模型中进行验证

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>
于 2016-03-04T17:43:04.757 回答
0

在控制器中保存之前进行清理清理接受_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
于 2017-11-02T14:03:23.903 回答