Originally I thought I needed accepts_nested_attributes_for, but that seems to be mostly for when we're creating the related objects, which I'm not--I need to save relationships to existing objects.
My example (github) uses a simple product/category relationship. We need to get a product's categories, we need to get a category's products. In the olden days, we would have used habtm; this example uses has_many:through. We'll use checkboxes to associate categories on the product's form page.
A number of posts were helpful during this process, although none was an SSCCE (at least to my new-again-to-Rails eyes). I don't know if I ended up doing this in the (or a) Rails Way; feedback is welcome. The nutshell solution for me ended up boiling down to the following:
* I didn't need accepts_nested_attributes_for (I'm referencing, not creating).
* This stackoverflow question provided form element hints.
* These posts pointed me away from habtm, although I'm still fuzzy on why (not _why).
* The second post above also reinforced form element naming.
(Note: I became uncertain about needing attr_accessible; the example works without it. In my original project it didn't, but it's likely I screwed something up the first time around and need to revisit it.)
Here's the Product, Category, and ProductCategory models, in all their tiny little glory.
class Product < ActiveRecord::Base validates :name, :presence => true has_many :product_categories has_many :categories, :through => :product_categories end class Category < ActiveRecord::Base validates :name, :presence => true has_many :product_categories has_many :products, :through => :product_categories end class ProductCategory < ActiveRecord::Base belongs_to :product belongs_to :category end
The migrations for Product and Category are what we'd expect. The mapping table migration is similarly trivial.
class CreateProductCategoriesTable < ActiveRecord::Migration def self.up create_table :product_categories, :id => false do |t| t.references :product t.references :category end add_index :product_categories, [:product_id, :category_id] add_index :product_categories, [:category_id, :product_id] end def self.down drop_table :product_categories end end
The checkboxes are created with the snippet below. It's kind of "manual" this way, there's likely a cleaner way using stock Rails.
<% Category.all.each do |cat| %> <%= cat.name %> <%= check_box_tag :category_ids, cat.id, @product.categories.include?(cat), :name => 'product[category_ids]' %> <% end %>
That's it: I suspect my original frenzied attempts, spinning through methodologies, got things out-of-sync and cost me a fair amount of time. Not needing attr_accessible is probably because I'm not using nested attributes. I'm not sure what's wrong with habtm. I'm not sure if there are performance penalties for using mapping classes. Lots to explore, which is both good and bad.