Handling multi-model forms in Rails
Sometimes (a lot of times, actually) I need to post a form that deals with several different models. For instance, in Golf Trac, fields_for "course[course_holes][]" passes an array of hole attributes that relate back to a course, where a course has_many holes. A course also has_many tees, so there’s a fields_for "course[course_tees][]", as well. This post isn’t about how to setup the forms, but more of what to do in the model.
Of course, I’m sure there are a lot of different ways to do this, but here’s my approach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class Course < ActiveRecord::Base has_many :holes, :dependent => :destroy has_many :tees, :dependent => :destroy def course_holes=(course_holes) set_model_attributes(:holes, course_holes) end def course_tees=(course_tees) set_model_attributes(:tees, course_tees) end private def set_model_attributes(model, model_attributes) model_attributes.each_with_index do |attributes, index| if attributes[:id].blank? send(model).build(attributes) else _model = send(model).detect {|m| m.id == attributes[:id].to_i} _model.attributes = attributes end end end end |
I’m going to need the set_model_attributes method in a few other places, so it’ll eventually end up in a module.

Chris Friday, 02 Nov, 2007 Posted at 06:05AM
I haven’t messed with it yet, but check out conductors. It might be just what you are looking for.
Ryan Sunday, 04 Nov, 2007 Posted at 02:14PM
It looks pretty cool. But the way I have things set up now, my controllers look exactly the same as they would if they were setup for only one model. I wrote a module with a few generic model helpers that assist with multiple associations, so I don’t know if I need something quite like a conductor (yet, anyway).
Plus, part of me thinks adding more layers to MVC kind of makes things unnecessarily complex. Of course, the presenter/conductor idea is probably for situations where it would be even more complex to not use.
Do you plan on using it?
Chris Sunday, 04 Nov, 2007 Posted at 03:13PM
I try to avoid forms that manipulate multiple models ;-p
I agree that an extra layer of abstraction can make it a bit more complex. However, I also would say that coupling the models too tightly can make them muddy and somewhat confusing. The benefit of a separate layer is that you keep model x from being responsible for setting up model y on a form submission.
If you think of a conductor as an abstract “super” model, then in makes sense in some ways. Then, you would simply create a controller that handles the new model.
In the end, I’m just a fence-rider, simply because I haven’t needed to deal with this situation. When/if I do, I’ll pick a side :-)
Ryan Sunday, 04 Nov, 2007 Posted at 06:18PM
I don’t think I’ve ever gotten into this multi-model form business before, but I’ve learned that it becomes a pain quick. I tried to come up with ways to avoid it, but there really isn’t one that I can see.
A golf scorecard has different tees, yardages, handicaps, pars, slope/rating, mens/ladies, general course info, and so on. I thought about going one step at a time, using Ajax to replace the current form when completed, but then editing becomes a pain. I think it would have been far too much Ajax, anyway. And going page-to-page to build the scorecard felt like a 1995 thing to do.
I suppose it’s just the nature of the beast.
It took me a while to see the value in the “presenter pattern” or whatever, and since this is the first I’ve heard of a conductor, maybe I just need a little more time for the idea to settle. I’m definitely open to other options, though.
Thomas Lee Tuesday, 11 Nov, 2008 Posted at 08:29AM
Hi,
I thought multi-model forms in Rails sucked too, so I wrote a plugin that changes the way the parameter parser works:
http://www.deskchecked.com/2008/07/20/taking-the-pain-out-of-complex-forms-in-rails/
It’s better suited to complex forms IMO, but still needs some development work.
Also get vocal on this patch if you’d like to see it in a future version of Rails:
http://rails.lighthouseapp.com/projects/8994/tickets/1142-simplified-parameter-parsing-code
Cheers, T