Ruby on Rails
HowToWorkWithCheckboxHABTMRelationships

I’m documenting a simple, fairly elegant method of handling simple has_and_belongs_to_many (habtm) relationships, for example assigning many tags to an individual article.

Note: Since Rails 0.13, this is supported in a more elegant way via automatically generated methods in the model as described here: CheckboxHABTM

This covers a helper method and its use in the view, and implementation in the controller, for the complete package deal.

Caveat: This is not the best code ever (far from it), I know, but it does work and it’s the best solution I’ve seen at this point. I figure that it’s better to put it out there as an example for folks than to wait til I make it better (e.g. just this side of never) and keep it to myself.

The Main Course – the Helper

This application helper will create a palatable checkbox group for your tags, authors, or whatever you will be creating a simple habtm relationship for—and when you view an existing article, book, or whatever, will automatically check the checkboxes that represent existing relationships.

This goes in your application helper (or in a particular controller’s helper):


    # would like to abstract this further. at some later date, maybe...
    def build_checklist_group(collection)        
        str="" 
        for tag in collection
            str << %{<input type="checkbox" name="tags[]" id="tag_#{tag.id}" value="#{tag.id}" }
            str <<  " checked='checked'" if @article.tags.include?(tag)
            str << %{/> #{tag.name} <br />}    
        end
        str
    end

Edit the names as necessary—although, if you’re working with tags, this will work nicely for you.

The View

You will of course be calling the helper from the relevant view, but first you must fetch the collection to feed it, in the controller method/action you are using that will generate the form that features the checkboxes :


        @tags = Tag.find_all

Then, in the view itself:


<b>Tags:</b>
        <%= build_checklist_group(@tags) %>

Saving & Editing Relationships

On to the controller.

Now, to save a new item with these tags, this is all that is necessary:


        # selected tags
        @article.tags << Tag.find(@params['tags'])

To edit an existing item with, presumably, existing tag relationships, it is 2 lines:


        # destroy removed tags
        @article.tags.delete(@article.tags - Tag.find(@params["tags"]))

        # add new ones
        @article.tags << Tag.find(@params['tags']) - @article.tags

The first line removes any relationships with tags that you unchecked. The second line ensures no duplicate relationships will be made with the checkboxes you left checked. I have little doubt that this could be refactored further, but I actually like it like this.


A start-to-finish tutorial on this subject exists here – jrh