Checkbox form in Ruby on Rails based on many-to-many relationship

ruby-on-rails, ruby-on-rails-3

Solution

With an association like this, DocumentType will have a `person_type_ids` method that returns an array of the ids of the associated person types.

Therefore if you create a set of check boxes where the name is `document_type[person_type_ids][]` and whose value is the id of each person type then what will be submitted is an array containing the ids of the checked person types, so

document_type.update_attributes params[:document_type]

Would update the association. The only subtlety here is that if no checkboxes are checked then no array is submitted (you can't submit an empty array) and the list of associated person types won't be cleared. I normally get around this by having a hidden field that adds the value 0 to the array (since I know that will never be a legitimate id) and then strip that 0 from the array in the controller.

Problem

My Ruby on Rails application has a DocumentType model and a PersonType model. A Document is of a DocumentType (eg Letter, Postcard) and lists one or more People. Each Person is assigned a PersonType to represent their relationship to a Document (sender, receipient, author etc.). A user is only allowed to assign People to the PersonTypes relevant to the DocumentType. Table definitions: ``` create_table "document_types", :force => true do |t| t.string "name" end create_table "document_types_person_types", :id => false, :force => true do |t| t.integer "document_type_id" t.integer "person_type_id" end create_table "person_types", :force => true do |t| t.string "name" end ``` Model Definitions: ``` class Document < ActiveRecord::Base has_many :people, :dependent => :destroy belongs_to :document_type end class DocumentType < ActiveRecord::Base has_many :documents has_and_belongs_to_many :person_types end class Person < ActiveRecord::Base belongs_to :document has_and_belongs_to_many :person_types end class PersonType < ActiveRecord::Base has_and_belongs_to_many :people has_and_belongs_to_many :document_types end ``` Table examples: ``` documents id: 1 document_type_id: 1 name: Letter from John Smith to Jane and Joe Smith document_types id: 1 | name: letter document_types_person_types document_type_id: 1 | person_type_id: 1 document_type_id: 1 | person_type_id: 2 document_type_id: 1 | person_type_id: 4 person_types id: 1 | name: Sender id: 2 | name: Recipient id: 3 | name: Photographer id: 4 | name: Author people id: 1 | document_id: 1 | name: John Smith | person_type_id: 1 id: 2 | document_id: 1 | name: Jane Smith | person_type_id: 2 id: 3 | document_id: 1 | name: Joe Smith | person_type_id: 2 ``` When a user is adding a Person to a Document, they will select what the relationship that Person is to the Document, and that will be stored within the Person model (or extended model). A Document will only present PersonTypes that are relevant to that DocumentType (for example, a Photograph won't have an Author, but will have a Photographer). So after all that...What I want to achieve is an admin interface for the DocumentTypes, representing the document_types_person_types table which shows all the PersonTypes and allows me to select a checkbox if a PersonType is applicable: ``` EDIT DocumentType Letter Sender [x] Recipient [x] Photographer [ ] Author [x] ``` Those PersonTypes selected will then be the only PersonTypes available when adding a Person to a Document. Hopefully more straight forward than my convaluted attempt at explaining may have represented. Could anyone provide any pointers? edit: For future reference, based on Frederick's answer below, this worked ``` <% for pt in PersonType.find(:all) %> <%= check_box_tag "document_type[person_type_ids][]", pt.id, @document_type.person_types.include?(pt) %> <%= pt.name %> <% end %> ```

Original source