Rails - сильные параметры с пустым массивом
Я посылаю массив идентификаторов ассоциации, скажем foo_ids
моему контроллеру. Чтобы разрешить массив значений, я использую:
params.permit(foo_ids: [])
Теперь проблема заключается в том, что если я отправлю пустой массив из foo_ids
, параметр будет проигнорирован. Вместо того, чтобы очищать все foos
по мере того, как должен работать пустой массив, ассоциация остается одна, потому что foo_ids
не разрешено.
Возможно, это связано с тем, что пустой массив преобразуется в nil в rails, и это значение nil игнорируется, поскольку сильные параметры ищут массив скалярных значений, а не одно скалярное значение.
Может ли кто-нибудь предложить хороший способ решить эту проблему? Спасибо!
Дополнительная информация
В действии контроллера обновлений мне нужно иметь возможность обрабатывать два случая. Мне нужно установить foo_ids
в пустой массив. Мне также нужно иметь возможность игнорировать foo_ids
, если я просто хочу обновить другое поле. Установка foo_ids
в пустой массив, если nil не работает для этого второго случая.
Ответы
Ответ 1
Временное решение, к которому я пришел, это:
params[:foo_ids] ||= [] if params.has_key?(:foo_ids)
params.permit(foo_ids: [])
Здесь foo_ids
устанавливается на пустой массив, только если он передан. Если он не передан в запросе, он игнорируется.
Я все еще надеюсь найти лучшее решение для этого, так как это будет довольно распространено в проекте, над которым я работаю - пожалуйста, предлагайте лучшие идеи, если у вас есть.
Ответ 2
Это довольно поздно, но у меня была эта проблема. Я решил это, включив в инструкцию разрешения как скалярную версию, так и версию массива:
params.require(:photo).permit(:tags, tags: [])
FYI - он имеет, чтобы иметь как в одном утверждении разрешения - если вы их связываете, он по какой-то причине будет выброшен.
EDIT: я только заметил, что пустой массив, представленный с помощью этого метода, будет преобразован в nil - у меня теперь есть куча полей, которые должны быть пустыми массивами, которые равны нулю. Поэтому решение, которое я разместил, на самом деле не работает для меня.
ИЗМЕНИТЬ второе: подумал, что я уже добавил это, но эта проблема связана с Rails, выполняющим deep_munge по хэшам параметров. В этом комментарии объясняется, как это исправить: fooobar.com/questions/99252/...
Ответ 3
У меня была такая же проблема в последнее время, но ни один из ответов здесь не работал у меня. Это мое решение. Если у вас есть javascript, обрабатывающий HTTP-запросы, это может сработать и для вас.
На стороне клиента:
if (photo.tags.length === 0){
photo.tags = ["null"]
}
И на вашем PhotosController
def photo_params
p = params.require(:photo).permit(tags: [])
p["tags"].reject! { |tag| tag == "null" }
p
end
Ответ 4
Это решение не будет работать во всех случаях:
params.require(:photo).permit(:tags, tags: [])
Например, если вы используете MongoDB и у вас есть массив tag_ids, в котором хранятся идентификаторы в коллекции has_many, ваш атрибут tag_ids ДОЛЖЕН быть массивом, если вы укажете "type: Array" для атрибута в вашей модели. Следовательно, отправка tag_ids со значением nil не будет работать, даже если вы сделаете это:
params.require(:photo).permit(:tag_ids, tag_ids: [])
Mongoid, официальный адаптер Ruby для MongoDB, будет жаловаться, что значение tag_ids должно быть массивом.
Решение в том, что вы действительно можете отправить пустой массив через форму! И это не обязательно должен быть запрос json. Вы можете просто использовать remote: true в своей форме и отправить его через тип:: js. Как это сделать? Просто. Просто добавьте скрытый ввод в форму и установите его значение в пустую строку:
<%= form_for @user, remote: true, html: { class: 'form' } do |f| %>
<%= select_tag("#{f.object_name}[tag_ids][]", options_for_select(Tag.all.collect {|t| [t.name, c.id]}, selected: f.object.tag_ids), { class: 'form-control', multiple: 'multiple' }) %>
<%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
<%= f.submit class: 'btn ink-reaction btn-raised btn-primary' %>
<% end %>
Вот этот ключ:
<%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>
Ваш атрибут будет сохранен как пустой массив в вашей базе данных. Обратите внимание, что я проверил это только с Mongoid, но я предполагаю, что он поддерживает те же функции в ActiveRecord.