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