Earlier today I was trying to figure out how to make a list of potential models to be created. For example: I have some
Users and some
Groups. I have a many-to-many
Membership join model with some
has_many :throughs. The fundamental goal is to provide the user an interface to add Membership models that link a
User to a
Group. I could use a bunch of checkboxes, but I’d rather pull up a list of possible
Groups with little “add” links next to them.
Since I’m using RESTful controllers, we have a
MembershipsController that implements all the standard methods. We’re also nesting our routes such that
MembershipsController nested beneath the
UsersController. In order to create the Membership, we need to POST to /users/1/memberships. But how do we get a list of
Memberships that we can quickly add?
How about modifying
MembershipsController.new? We don’t need the normal definition of
new, since we’re never going to be manually creating a new Membership.
# controllers/memberships_controller.rb def new @memberships = Groups.find(:all).collect do |group| Membership.new(:user_id => params[:user_id], :group_id => group.id) end end
Now we have a list of potential
Membership objects available to our view. Remember, the
Memberships haven’t been saved yet. They’re just there for convenience for holding attributes. We are doing object-oriented programming after all.
# views/memberships/new.html.erb <% @memberships.each do |membership| %> <% form_for :membership, membership, :url => memberships_path do |f| %> <%= f.hidden_field :user_id %> <%= f.hidden_field :group_id %> <%= f.submit membership.group.name %> <% end %> <% end %>
Basically what we have here is a big list of predefined join models wrapped up in form tags. When you click on one of them, you’ll end up submitting the form that actually creates the model. Eventually, we could make this into an AJAX widget that uses
form_remote_for with very little effort.
Nothing I have described here is particularly revolutionary. Rather than just returning a single new
Membership in the
new method, we return a list of objects. Rather than rendering one form with editable fields, we render multiple forms with predefined, hidden fields. Whichever you submit creates the corresponding object. These two very simple changes to the standard REST actions allow us to easily and elegantly create our join models.