Absolute Moron's Guide to Forms in Rails, Part 2 6

Last time, we created a Flight resource and took a peek at the generated scaffolding code. In particular, we opened up the new.html.erb template. Today we’re going to take a closer look at the code in that template to understand how it really works.
Recall one of the core concepts we established last time: requests from the browser travel over a bridge and into your controller code. That bridge is the params hash, generated for you by Rails itself, and is your code’s window to the outside world.
With that concept in mind, and without further ado…
The “New” form submits to the “Create” action
Rails views that end with the suffix .html.erb are designed to generate html, which in turn will be sent back to the browser. Let’s look at the new.html.erb file again:
<% form_for(@flight) do |f| %>
<p>
<b>Number</b><br />
<%= f.text_field :number %>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
The .erb extension indicates “embedded Ruby”, and indeed we can see Ruby code that’s been embedded into the surrounding HTML landscape. The Rails pipeline executes the Ruby code, and then sends the entire resulting HTML back to the browser. To see what got generated, use the View Source feature of your browser and locate the <form>:
<form action="/flights" class="new_flight" id="new_flight" method="post">
<div style="margin:0;padding:0"><input name="authenticity_token" type="hidden" value="025e8a2b92e325ea0a34ecf040693c0f74262a41" /></div>
<p>
<b>Number</b><br />
<input id="flight_number" name="flight[number]" size="30" type="text" />
</p>
<p>
<input id="flight_submit" name="commit" type="submit" value="Create" />
</p>
</form>
Let’s zoom in on the form element itself:
<form action="/flights" class="new_flight" id="new_flight" method="post">
That’s the HTML tag for creating a form. In HTML, the action parameter is required and tells the browser what to do when the user clicks the submit button.
- We want the browser to call our
createaction, so we use the url/flights - Similarly,
methodtells the browser which HTTP verb to use. In this case, we’re using POST
Those of you who paid attention during our REST series will recall that for a resource named Flight, the url /flights with a method of POST will trigger the create action in a controller named FlightsController in our Rails application.
form_for and More
Now, how did we generate this form tag with these attributes? We did it with the form_for method in Rails:
form_for(@flight) do |f|
form_for is only useful for creating a form to act upon an ActiveRecord-derived model, but that’s the 80% case when developing Rails applications. Suffice to say, you’ll be using form_for a lot.
form_for knew how to generate the <form> tag and all of its attributes by examining the flight variable. Recall how this variable was created by the controller’s new action:
@flight = Flight.new
Since it’s a new Flight instance, and has not yet been saved to the database, form_for knows that we want to make a form for creating a new flight in the database, not for updating an existing flight record. Therefore, our HTML form will want call the create action; and as a result, the url must be /flights and the method must be set to POST.
(Yes, if you’re not following RESTful controller conventions, you can override the url and method yourself; but don’t expect me to help you with that: you can go look it up yourself, you non-conformist, you.)
Back in Part 1, we explained that between the browser and your controller code is a bridge. The bridge is built mostly of your routes.rb file, which enables Rails to transform incoming HTTP requests into a single Ruby hash called params.
Let’s zoom in on that textbox in our form. First, look at the Ruby code in our template:
<%= f.text_field :number %>
which became this HTML
<input id="flight_number" name="flight[number]" size="30" type="text" />
How did it do that and why?
- We are using the block variable,
f. fis given to us byform_forand knows how to generate form elements for us.fis called a form builder.- We are calling the
text_fieldmethod, which specifically knows how to generate aninputtag withtype="text". - The first parameter,
:number, helps it generate a theidandnameattributes of theinputtag.
The id attribute is helpful for CSS and DOM access, but has no bearing on what happens when the form is submitted. The name attribute, on the other hand, is very important. Rails will automatically populate the params hash with data coming in from the form. It uses the name attribute of the input element as the key in our hash.
If this doesn’t make sense right away, let’s take a step back for a moment. Suppose our HTML looked like this instead:
<input id ="my_id" name="sport" type="text">
This creates a text field named sport. Suppose further that we typed the value “hockey” into this text field and submitted the form. What would happen? Rails would add a key of “sport” with the value “hockey” into the params hash, and we could easily discover what was typed into the text field like this:
def create
favorite_sport = params[:sport]
# favorite_sport is now 'hockey'
...
end
Now for the cool part. You can isolate all of your form’s data into your very own hash inside the params hash by following a simple convention. In the name attribute of your input tag, use square brackets. The part before the brackets will be name of your hash, and the part inside will be the key into your hash. That’s what f.text_field :number did for us:
<input id="flight_number" name="flight[number]" size="30" type="text" />
Here, the name is flight[number]. When Rails creates the parameter values for us, it will create a nested hash called flight, and inside that hash, it will create a key called number, and its associated value will be whatever was typed into the form as the flight number. The big payoff for all of this? params[:flight] will give us a hash of all of the key/value pairs submitted by our form.
We can now understand why this code that was generated in our create action:
@flight = Flight.new(params[:flight])
This creates a new Flight object, instantly initialized with all of the data coming from our form.
The Catch
In order for this magic to work, you have to be careful how you use the form builder object. The first parameter must be the name of an attribute on your model (that is, a column in your table). Rails not only uses that attribute to pre-fill the form for you based on the model object’s initial state, but the Flight.new(params[:flight]) code is expecting attribute/value pairs that correspond only to your model’s attributes.
What About Other Controls?
We’ve traced the lifecycle of a textbox from Ruby template, to HTML, to params hash, to controller code. In the next article we’ll learn how to use the same form builder object to easily bind checkboxes, radio buttons, and list boxes to our model as well.



This is great. Will you also cover forms where you need to update multiple models in the same form?
What we have alone been able to show is that our understanding can not take account of our understanding; on the other hand, our sense perceptions are the mere results of the power of our a posteriori knowledge, a blind but indispensable function of the soul.
By means of analytic unity, the phenomena, in other words, occupy part of the sphere of the Ideal of human reason concerning the existence of our analytic judgements in general; thus, philosophy would thereby be made to contradict, in the full sense of these terms, the noumena.
Very very good! I’m learning a lot about rails just by reading your posts in a few minutes! this is fantastic! thank you! =D
Great article, but I have a question for this new form builder: Is there anyway for me to retrieve the generated form id back so I could play with it on other javascript functions?
Could you put a link to the next installment at the bottom of the pages? :)