Ruby-on-rails Rails 4.0 强参数嵌套属性,带有指向哈希的键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14483963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:19:12  来源:igfitidea点击:

Rails 4.0 Strong Parameters nested attributes with a key that points to a hash

ruby-on-railsrubyruby-on-rails-4strong-parameters

提问by John

I was playing around with Rails 4.x beta and trying to get nested attributes working with carrierwave. Not sure if what I'm doing is the right direction. After searching around, and then eventually looking at the rails source and strong parameters I found the below notes.

我在玩 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.
# 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

https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb

So its saying you have to specify every single every single attribute within the has, I tried the following:

因此,它说您必须指定 has 中的每个属性,我尝试了以下操作:

Param's example:

参数示例:

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

Controller

控制器

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

The above isn't "working" (its not triggering carrierwave) however I am no longer getting errors (Unpermitted parameters: filename) when using the standard nested examples I found ex:

以上不是“工作”(它没有触发载波)但是当使用我发现的标准嵌套示例时,我不再收到错误(不允许的参数:文件名):

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

If anyone could help it would be great. I was not able to find a example with nested with a key that points to a hash.

如果有人可以提供帮助,那就太好了。我无法找到嵌套有指向散列的键的示例。

采纳答案by Jim Deville

My other answer was mostly wrong - new answer.

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

in your params hash, :filename is not associated with another hash, it is associated with an ActiveDispatch::Http::UploadedFile object. Your last code line:

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

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

is actually correct, however, the filename attribute is not being allowed since it is not one of the permitted scalar types. If you open up a console, and initialize a params object in this shape:

实际上是正确的,但是,不允许使用 filename 属性,因为它不是允许的标量类型之一。如果您打开一个控制台,并以这种形状初始化一个 params 对象:

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

and then run it against your last line:

然后针对你的最后一行运行它:

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

However, if you do the same against a params hash with the uploaded file, you get

但是,如果您对上传文件的 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"=>{}}}

So, it is probably worth a bug or pull request to Rails, and in the meantime, you will have to directly access the filename parameter using the raw paramsobject:

因此,可能值得向 Rails 提出错误或拉取请求,同时,您必须使用原始params对象直接访问文件名参数:

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

回答by Pat McGee

So, you're dealing with has_many forms and strong parameters.

因此,您正在处理 has_many 形式和强参数。

This is the part of the params hash that matters:

这是重要的 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">
 }
}

when you define strong parameters like this...

当你定义这样的强参数时......

permit(:assets_attributes => [:filename]) 

Things break, because where rails expects a filenameit's getting this "0"

事情坏了,因为 rails 期望filename它得到这个"0"

What does that number mean? It's the idfor the asset you are submitting via your form. Now initially you might think you have to do something like

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

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

This looks like it follows other strong parameters syntax conventions. However, for better or for worse, they have made things a little easier, and all you have to write is:

看起来它遵循其他强参数语法约定。然而,无论好坏,它们让事情变得更容易了,你所要做的就是:

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

Edit- As jpwynnpointed out in the comments, in Rails 4.2.4+ the correct syntax is

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

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

and that should work.

这应该有效。

When you hit walls with strong params, the best thing to do is throw a debugger in your controller and test things out. params.require(:something).permit(:other_things)is just a method chain so you can try out different things on the full params hash until you find what works.

当你用强参数撞墙时,最好的办法是在你的控制器中放置一个调试器并测试一下。params.require(:something).permit(:other_things)只是一个方法链,因此您可以在完整的 params 散列上尝试不同的东西,直到找到有效的方法。

回答by cgat

try

尝试

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

I had this issue about a month ago and some searching around dug up this solution. It was adding the :id or :screenshot_id that fixed the problem (or both, I can't remember). This works in my code though.

大约一个月前我遇到了这个问题,一些搜索找到了这个解决方案。它添加了解决问题的 :id 或 :screenshot_id (或两者,我不记得了)。不过,这适用于我的代码。

回答by nothing-special-here

Actually there is a way to just white-list all nested parameters.

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

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

This method has advantage over other solutions. It allows to permit deep-nested parameters.

这种方法优于其他解决方案。它允许允许深度嵌套的参数。

While other solutions like:

而其他解决方案如:

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

Don't.

别。



Source:

来源:

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

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

回答by Tunde Adetula

I had same problem just got it fixed now all you have to do is

我遇到了同样的问题,现在你所要做的就是解决它

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

Use pry gem to see what kind of attributes your asset object has makes sure theres an id and add other missing attribute, that should then work perfectly. Am using paperclip assets is my nested object inside the vehicle class and an attachment of images is added to the asset. make sure you do the validation in the model

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

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

In your view loop through the asset to get each image

在您的视图中循环遍历资产以获取每个图像

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

If am correct your problem is that asset_attributes is an array with each image having an index column and an image

如果我是正确的,你的问题是 asset_attributes 是一个数组,每个图像都有一个索引列和一个图像

Your form_for should have something similar to this and if you want you can also include preview so the upload can view their images use the bottom code for that

你的 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>

回答by gilcierweb

Sanitize before save in controller Sanitize accepts_nested_attributes_for attributes with index.

保存在控制器中之前进行清理 清理接受_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