Monday, September 21, 2015

How to Save Multiple Checkbox Values to a Database in Rails

Imagine you have a form in your Rails app which is backed by an ActiveRecord model. In this form there are a bunch of checkboxes, the values of which you want to persist to your database. How do you go about handling this scenario?

As ever, the code for this article can be found on our GitHub repo.

The Anti-Pattern

Well, an initial reaction might be to create a string column in the database to hold all of the checkbox data. You could then use a before_save hook in the model to build the string and use check_box_tag helpers in the view to display the check boxes.

Let’s have a quick look at what this might look like. To do so, we’ll create a demo app into which you can enter the name of a professor and select their various areas of expertise.

rails new cb-demo && cd cb-demo
rails g scaffold professor name:string expertise:string
rake db:migrate

After that open up /app/views/professors/_form.html.erb and replace:

<%= f.label :expertise %><br>
<%= f.text_field :expertise %>

with:

<%= label_tag 'expertise_physics', 'Physics' %>
<%= check_box_tag 'professor[expertise][]', 'Physics', checked('Physics'), id: 'expertise_physics' %>

<%= label_tag 'expertise_maths', 'Maths' %>
<%= check_box_tag 'professor[expertise][]', 'Maths', checked('Maths'), id: 'expertise_maths' %>

In /app/controllers/professors_controller.rb alter:

params.require(:professor).permit(:name, :expertise)

to:

params.require(:professor).permit(:name, expertise:[])

Then in /app/models/professor.rb add:

before_save do
  self.expertise.gsub!(/[\[\]\"]/, "") if attribute_present?("expertise")
end

And in /app/helpers/professors_helper.rb add:

def checked(area)
  @professor.expertise.nil? ? false : @professor.expertise.match(area)
end

Finally, run rails s and navigate to http://localhost:3000/professors

And as you can see, it works. But unfortunately, that’s about all it does. Saving checkbox data to the database this way will just cause problems further down the road. For example as the number of professors and the number of areas of expertise grow, queries to find out which profs are assosciated with which areas will become a horrific mess.

Also what happens if you want to delete or rename an area of expertise? In this case you’d have to manipulate the database directly, which is almost never a good thing (not to mention time consuming and error-prone).

The Right Way

Luckily, there is a much better way to accomplish this — namely by moving Expertise into its own model and declaring a has_and_belongs_to_many association between Expertise and Professor. This will create a direct many-to-many connection between the models (by means of a join table — a database table that maps two or more tables together by referencing the primary keys of each data table).

As the Rails guide states:

A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way:

You can visualize it like so (where expertises_professors is the join table):

Diagram illustrating HABTM association

Continue reading %How to Save Multiple Checkbox Values to a Database in Rails%


by James Hibbard via SitePoint

No comments:

Post a Comment