Rails - Strong parameters with empty arrays

This solution won't work for all cases:

params.require(:photo).permit(:tags, tags: [])

For example, if you are using MongoDB and you have a tag_ids array, which stores the ids in a has_many collection, your tag_ids attribute MUST be an array if you specify "type: Array" for the attribute in your model. Consequently, it won't work to send tag_ids with a nil value even if you do this:

 params.require(:photo).permit(:tag_ids, tag_ids: [])

Mongoid, the official Ruby adapter for MongoDB, will complain the value of tag_ids must be an array.

The solution is you can indeed send an empty array via your form! And it doesn't need to be a json request. You can simply use remote: true on your form and send it via type: :js. How to do it? Simple. Just add a hidden input in your form and set its value to an empty string:

<%= 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 %>

This here is the key:

<%= hidden_field_tag "#{f.object_name}[tag_ids][]", '' %>

Your attribute will be stored as an empty array in your database. Note I only tested this with Mongoid, but I assume it carries the same functionality in ActiveRecord.


This is quite late, but I just had this problem myself. I solved it by including both the scalar version and array version in the permit statement, like so:

params.require(:photo).permit(:tags, tags: [])

FYI - it has to have both in the same permit statement - if you chain them it'll get thrown out for some reason.

EDIT: I just noticed that an empty array submitted via this method will be turned into nil - I've now got a bunch of fields that should be empty arrays that are nil. So the solution I posted doesn't actually work for me.

EDIT the second: Thought I had already added this, but this problem is related to Rails performing deep_munge on params hashes. This comment explains how to fix it: https://stackoverflow.com/a/25428800/130592


The temporary solution I've come down to is:

params[:foo_ids] ||= [] if params.has_key?(:foo_ids)
params.permit(foo_ids: [])

Here, foo_ids is set to an empty array only if is passed. If it is not passed in the request, it is ignored.

I'm still hoping to find a better solution to this, as this sort of thing will be quite common in the project I'm working on - please do suggest better ideas if you have any.