Search code examples
ruby-on-railsrubypostgresqljsonb

Storing array data in jsonb field with Rails and Postgresql


Say I have a Car model in which I want to display different kinds of data. I add a data column to the table:

class AddDataToCars < ActiveRecord::Migration[5.0]
  def change
    add_column :cars, :data, :jsonb, null: false, default: '{}'
    add_index  :cars, :data, using: :gin
  end
end

Then I add an accessor for a field I want to be present (extra_options):

class Car < ApplicationRecord
  store_accessor :data, :wheel_count, :extra_options

  validates :extra_options, presence: true
end

I make sure it's a permitted parameter in cars_controller.rb:

def car_params
  params.require(:car).permit(:make, :model, :data, :wheel_count, :extra_options)
end

Now I can create a text input in my _form.html.erb partial that puts data in wheel_count like this:

<div class="field">
  <%= f.label :wheel_count %>
  <%= f.text_field :wheel_count %>
</div>

And it works as expected. What I then would like is a select list (multiple select) or a set of checkboxes where the selected options are stored under extra_options.

I've tried with:

<%= f.select :extra_options, options_for_select(["1", "2", "3", "4", "5"], car.extra_options), { include_blank: true }, { multiple: true } %>

which produced the following markup:

<input name="car[extra_options][]" type="hidden" value="" />     
<select multiple="multiple" name="car[extra_options][]" id="car_extra_options">
  <option value=""></option>
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
  <option value="5">5</option>
</select>

Obviously the option labels should make more sense than just 1, 2, 3, 4 and 5, but what's worse is that nothing gets stored when I submit the form.

On submission if I select 2 and 3 the parameters look like:

"extra_options"=>["", "2", "3"]

which results in the message Unpermitted parameter: extra_options so it seems like I am not allowed to put arrays in this field.

It may seem stupid to create a jsonb field just for some extra options but it would naturally also hold all sorts of other data.

So how can I create a multiple select list or preferably a set of checkboxes that saves the data correctly in the jsonb field (and of course when editing a submission shows the already selected/checked items)?


Solution

  • Try this, in car_params

    params.require(:car).permit(:make, :model, :data, :wheel_count, extra_options: [])
    

    For checkboxes, try this

        <%= hidden_field_tag "car[extra_options][]", [] %>
        <% ["1", "2", "3", "4", "5"].each do |o| %>
          <% default_checked = car.extra_options.include?(o.to_s) rescue false %>
          <label class="rightpad">
            <%= check_box_tag "car[extra_options][]", o, default_checked %>
          </label>
          <span>
            <%= o %>
          </span>
        <% end %>