Doing Checkbox Adds with HABTM
For some of you this may be a “what?” kind of post. But it’s something that other people playing with the Ruby on Rails framework might be able to use.
I’ve spent a while developing a production pipeline for Meticulous and I am thankful that I am not trying to build this beast in PHP. But Rails is relatively young (and rapidly changing) and that limits the amount of existing info for the newbies in the crowd. So doing an insert to a join table isn’t exactly something they run through in Agile Development with Rails.
So let’s say you’ve been making an app and you have a has_and_belongs_to_many relationship between two models. For the sake of this exercize, we’re going to use the Manuals model that we’re creating in the Koji Production Pipeline. Manuals would be user manuals. Various manuals generally belong to one department, however, there’s a lot of hardware and software that is shared between departments. We want to be able to show, on the department page, any manuals that belong to it. Simple enough, right?
Your schema/migration would look something like this:
create_table "departments_manuals", :id => false do |t|
t.column "manual_id", :integer, :limit => 10, :default => 0, :null => false
t.column "department_id", :integer, :limit => 10, :default => 0, :null => false
end
and then your models…
class Manual < ActiveRecord::Base
has_and_belongs_to_many :departments
end
class Department < ActiveRecord::Base
has_and_belongs_to_many :manuals
end
Once this is setup, it’s pretty much view hacking to get this to play nice. In your form partial for manuals (which is used in new and edit for scaffolded code), you basically get something setup that will return a list of departments and an applicable check box.
<p>What Departments should this appear in?</p>
<ul>
<% Department.find(:all, :order => "name ASC").each do |d| %>
<li><%= check_box_tag 'manual[department_ids][]', d.id, @manual.departments.include?(d) %>
<%= d.name %></li>
<% end %>
</ul>
The check_box_tag is a sexy little piece of code (full details in the api) that basically creates a check box with all the trappings. You can call a :checked => 1 is you want in the form tag helper and it will auto check the box, but then you’d be assuming that all the boxes in the array were checked.
<li><input id="manual[department_ids][]" name="manual[department_ids][]" type="checkbox" value="5" />Animation</li>
etc. etc.
So basiclaly, the code finds all the departments, orders them by the :order conditional statement (in this case by name in ascending order) and then loops through each one, echoing a list item with a checkbox followed by the department name.
Simple. Sexy. Took forever to figure out the first time (and a lot of questions in the #ror channel). Anyways. Hopefully that can help someone else out when they run into a similar issue.
Back to the code!

October 22, 2006 at 5:40 AM
This has been very helpful!
November 04, 2006 at 8:01 PM
I'm working with Rights/roles for authentication. I loop through all rights, printing out name/controller/action. Before the loop ends, I do another loop using your code to grab the checkbox status for 7 roles. I put the _form.rhtml in my admin folder since I'm currently in the admin index method (didn't know how to call a partial for another controller). It works great! I now get about 20 rows of rights with 7 columns on the right that show the correct state of roles assigned to each right.
I have one little weirdness though. On that same layout page, I can create a new right, but it takes the last right's row of role checkboxes as input, setting the exact roles that the last right had! Also, In the browser source code, every checkbox has the same name, same id, but dif values from 1~7? If I somehow add checkboxes to the 'new' form, will that take care of it defaulting to what the last row's roles were?
Lastly, (given that I am in the admin controller#index looping through rights, then roles...) how would I go about updating a checkbox's status in the database? (ie.in the join table) I want to uncheck/check a box and have an "onChange" event fire off a record update in the database. This would match the conenience of the rest of the rights form that uses in_place_editing_field to update any column in any row without leaving the overall form.
November 10, 2006 at 1:48 PM
Randy -
I know this code might be getting stale with newer versions of rails, but I haven't yet updated the pipeline code that runs this. Refactoring time for me it seems :)
In regards to your loop, so if the first six are a value of 0 and the last one is a value of 1, on submit makes them all 1 in the boolean? It seems there's some weirdness and bugginess in my code in regards to how it's playing with ActiveRecord.
There is a more extensive post now on the WIKI":http://wiki.rubyonrails.org/rails/pages/CheckboxHABTM that seems to have been updated quite recently. It looks like Stephen Caudill also has posted a helper for checkbox habtm that he created in August 06.
March 07, 2007 at 8:15 AM
It appears necessary to also have an input type="hidden" name="manual[department_ids][]" value="" tag somewhere if you want to be able to allow the user to select nothing in your list. Otherwise when updating the object, if they uncheck every previously selected item, nothing in the habtm is updated because no manual[department_ids][] values are in params[].
March 07, 2007 at 1:31 PM
Thanks Ben. Textile broke up your code, if you surrond it with @ it renders it.
@input type="hidden" name="manual[department_ids][]" value=""@
is what I think you had submitted.
April 26, 2007 at 5:44 AM
Thank you jathayde and Ben.
Without the hidden input it's not possible to select "nothing". However, when I add it I get the error
Couldn't find all Departents with IDs ('1','')
ActiveRecord tries to find the Department with id ''.