Form Nesting with `fields_for` (Week 5, Day 4)
So again today we worked solo, and again we worked on nested user input forms (i.e. a form within a form within a form... ad nauseum).
Seeing that the assignment was strangely short and simple, and that I had freedom to choose my own approach (no pair to worry about), I decided that I would take the opportunity to ditch direct HTML and acquaint myself with Rails' form helpers.
It really is a lot cleaner to use these, though they're also more complicated, so it took me a while to get it working. Some people had finished the project by lunch, whereas I took until 3:45. The problem I got stuck on for the longest time had to do with the fields_for method.
cat .non-rubyists-ignore
Let's take a look at my code:
<%= form_for(@newspaper) do |f| %> ... <h2>Subscriptions:</h2> <%= f.fields_for "subscriptions_attributes" do |subs_fields| %> <% @newspaper.subscriptions.each do |sub| %> <%= f.fields_for sub do |sub_fields| %> <%= sub_fields.text_field :price %> ... <% end %> <% end %> <% end %> <%= f.submit %> <% end %>
My intention was to mass-assign Subscription objects, which were in a different table than the Newspaper object which was being processed in the form. To do this, I needed an association between the tables, an accepts_nested_attributes_for :subscriptions set up in my Newspaper model, along with subscriptions_attributes added to attr_accessible.
All of this I had done, so in form above, I was trying to get a nested parameters hash for price that looked like this:
newspaper[subscriptions_attributes][X][price]
where X was a set of unique dummy keys. But I wasn't getting unique keys, I was getting this:
newspaper[subscriptions_attributes][subscription][price]
I tried adding an index, but that gave me too many keys:
newspaper[subscriptions_attributes][subscription][X][price]
As it turns out, Rails documentation is pretty sparse out here on the margins. It took me a fair amount of trial, error, and internet trawling before I finally found my answer here. As it turns out, I only needed one loop for the three I had tried.
<%= f.fields_for "subscriptions_attributes" do |subs_fields| %> <% @newspaper.subscriptions.each do |sub| %> <%= f.fields_for sub do |sub_fields| %>
became simply:
<%= f.fields_for :subscriptions do |sub_fields| %>
And thus, we see Rails magic at work yet again. Passing a symbol in to fields_for automatically associates that part of the form with the corresponding method on the form_for object, and Rails will find the appropriate keys to make that assignment work.
So in this case, the symbol :subscriptions will be used to call @newspaper.subscriptions and pre-populate the form with any relevant data, and because it's a collection, Rails knows to iterate through it and add unique dummy keys. Then Rails will assign the appropriate input names (since we'll be assigning subscriptions, the names will have to include subscriptions_attributes plus a dummy key).
I didn't understand this when I was fiddling around with specific key names and loops, but of course, that's the problem with Rails. It will do a lot of work for you, but it isn't always obvious how you get it to do that work. Rails is magic... if you know the right spells to cast!











