Подтвердить что ты не робот

Rails - сильные параметры с пустым массивом

Я посылаю массив идентификаторов ассоциации, скажем foo_ids моему контроллеру. Чтобы разрешить массив значений, я использую:

params.permit(foo_ids: [])

Теперь проблема заключается в том, что если я отправлю пустой массив из foo_ids, параметр будет проигнорирован. Вместо того, чтобы очищать все foos по мере того, как должен работать пустой массив, ассоциация остается одна, потому что foo_ids не разрешено.

Возможно, это связано с тем, что пустой массив преобразуется в nil в rails, и это значение nil игнорируется, поскольку сильные параметры ищут массив скалярных значений, а не одно скалярное значение.

Может ли кто-нибудь предложить хороший способ решить эту проблему? Спасибо!

Дополнительная информация

В действии контроллера обновлений мне нужно иметь возможность обрабатывать два случая. Мне нужно установить foo_ids в пустой массив. Мне также нужно иметь возможность игнорировать foo_ids, если я просто хочу обновить другое поле. Установка foo_ids в пустой массив, если nil не работает для этого второго случая.

4b9b3361

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