【问题标题】:Rails 4.0 Strong Parameters nested attributes with a key that points to a hashRails 4.0 强参数嵌套属性,其键指向哈希
【发布时间】:2013-01-07 04:23:39
【问题描述】:

我正在使用 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)

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

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 strong-parameters


    【解决方案1】:

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

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

    def screenshot_params
      params.require(:screenshot).permit(:title, assets_attributes: :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 哈希执行相同操作,则会得到 ​​p>

    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]
    

    【讨论】:

    • 再次感谢您的帮助。我会做的,至少我现在有一种解决方法。
    • 在 rails 4.0.0rc1 中似乎仍然是这种情况(至少对我而言),而且很糟糕。
    • 我在使用carrierwave时遇到了这个问题。有消息吗?
    • 这应该是 CW NF 中的“最先进”:github.com/firedev/cw_nf_rails4
    • 在最新版本中,最后描述的问题不再存在。对我来说,至少上传的文件不会被扔掉。另外:注意错字。上传文件 -> 上传文件
    【解决方案2】:

    所以,您正在处理 has_many 表单和强参数。

    这是参数哈希中重要的部分:

    "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 在 cmets 中指出的那样,在 Rails 4.2.4+ 中,正确的语法是

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

    这应该可行。

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

    【讨论】:

    • 在 Rails 4.2.4 中,后面的语法示例应该是 permit(:assets_attributes => [:id, :filename]) not permit(:assets_attributes => [:asset_id, :filename]),例如型号名称在 'id' 之前没有前缀
    【解决方案3】:

    试试

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

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

    【讨论】:

      【解决方案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

      【讨论】:

      • 是的,同样,如果您像我一样在某个对象上有 50 个字段,则此解决方案非常好。如果您真的没有需要担心的重大“暴露”问题,那么维护一个巨大的白名单 attrs 列表是非常烦人的。
      • 这完全违背了强参数的安全目的。
      【解决方案5】:

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

      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>
      

      【讨论】:

        【解决方案6】:

        在控制器中保存前清理清理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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多