The Wordchuck Blog.

Shelly Roche is a Ruby on Rails engineer and founder of Wordchuck. She usually blogs about Ruby, Rails 3, woodchucks, i18n, l10n, startup life or whatever has her fired up on any given day. Yes, she is available for i18n consulting & training. Find her or by .

How to use check_box in nested model forms

by Shelly

This article will show you how to use the check_box helper in a nested model form using Rails 3.

In this example, I have two models with a many-to-many relationship - projects and languages

Because a project can have many languages and a language can have many projects, I'm going to use a :has_many association with the :through option and create a join model called project_languages to store the relationships:

class Language < ActiveRecord::Base  
  has_many :project_languages
  has_many :projects, :through => :project_languages
end
class Project < ActiveRecord::Base
  has_many   :project_languages
  has_many   :languages, :through => :project_languages
end

The join model:

class ProjectLanguage < ActiveRecord::Base
  belongs_to :project
  belongs_to :language
end

We need to do one more thing to set our models up properly - tell the project model to automatically include the project_languages model when it saves. To do this, we add accepts_nested_attributes_for to the model like so:

class Project < ActiveRecord::Base
  has_many   :project_languages
  has_many   :languages, :through => :project_languages

  accepts_nested_attributes_for :project_languages, :allow_destroy => true
end

Using accepts_nested_attributes_for means we don't need to add anything special in the projects controller - it will automatically save the project_languages object when we save the project object.

In the edit form for my project, I want to list all of the languages that have been associated with the project, and then allow the user to check the box next to each language to set a flag in my nested project_languages model.

To give you more context for this functionality, part of the OpenGab awesomeness is that we have a rake task that automatically generates and deploys all of your translated .yml files. The flag we're setting with each check box indicates that all of the content for that language has been translated and is ready to be deployed to the production environment for the project. Languages that aren't checked remain associated with the project (i.e. the association in the project_languages model is not destroyed), but they will be ignored when the rake task to re-generate the yml files runs for the production environment.

The next step is to create the form in my project edit form view (this example is using Rails 3):

=form_for @project, :remote => true do |form|  

Then within the form:

-for language in @project.project_languages
  =form.fields_for :project_languages, language do |pl|
    =pl.check_box :production
    =pl.label language.language.name
    %br

For each language that has been added to the project (through the nested project_languages join model), we're printing a check_box that will update an attribute in the project_languages model called "production".

This is what it looks like in the form:

This is the html that is generated:

"project[project_languages_attributes][0][production]" type="hidden" value="0" />"project_project_languages_attributes_0_production" name="project[project_languages_attributes][0][production]" type="checkbox" value="1" />

And that's it!