Rails 4.0 Сильные параметры Вложенные атрибуты с ключом, указывающим на хэш

Я играл с Rails 4.x beta и пытался вставить вложенные атрибуты, работающие с носителем. Не уверен, что то, что я делаю, - правильное направление. После поиска, а затем, в конце концов, глядя на источник рельсов и сильные параметры, я нашел следующие примечания.

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

Вышеупомянутое не работает (его не запускает несущую волну), но я больше не получаю ошибок (Unpermitted parameters: filename) при использовании стандартных вложенных примеров, которые я нашел ex:

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

Если кто-то может помочь, это будет здорово. Я не смог найти пример с вложенным ключом, который указывает на хэш.

Ответ 1

Мой другой ответ был в основном неправильным - новый ответ.

в вашем параметре hash: 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]

Ответ 2

Итак, вы имеете дело с 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]) 

Вещи ломаются, потому что, когда рельсы ожидают 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) - это всего лишь цепочка методов, поэтому вы можете попробовать разные вещи в полном палитре params, пока не найдете то, что работает.

Ответ 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

Ответ 5

У меня была такая же проблема, что и сейчас, теперь все, что вам нужно сделать, это

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

Используйте pry gem, чтобы узнать, какие атрибуты ваш объект объекта удостоверяет, что theres 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 - это массив с каждым изображением, имеющим индексный столбец и изображение

У вашей формы_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

Санитировать перед сохранением в контроллере Sanitize accepts_nested_attributes_ для атрибутов с индексом.

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