The form_for
helper method sets up the entire form, creating the HTML
form
element but also
providing context for all of the fields in the form. The form_for
method is a bit sneaky, too. Both the
new.html.erb view and the edit.html.erb view use form_for
the same way:
<% form_for(@person) do |f| %> ... <% end %>
However, the generated form
element looks very different, depending on what exactly is in @person
. If @person
is just an empty object structure,
form_for
will work on the assumption
that this is to create a new object. If @person
actually contains data, however,
form_for
will assume that its form is
editing that object and create a different-looking form
element, plus a hidden field to enable
Rails’ REST capabilities.
When given an empty @person
object, form_for
prepares for a new
person:
<form action="/people" class="new_person" id="new_person" method="post">
<div
style="margin:0;padding:0"><input name="authenticity_token" type="hidden"
value="f80a01b9f14d38e0816877e832637e3cc9e668a1" /></div>
Note that the action
goes to people,
generically. The class
and id
reflect a new person, and the method is
simply post
.
When given an @person
object
with content, however, form_for
switches to editing a person:
<form action="/people/1" class="edit_person" id="edit_person_1" method="post">
<div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" />
<input name="authenticity_token" type="hidden" value="f80a01b9f14d38e0816877e832637e3cc9e668a1" /></div>
The action now goes to a URL that includes the ID of the object
being edited, and the class
and
id
attributes change values. The
method stays at post
—but the hidden
input
with the name _method
almost immediately after the form
is there to indicate that it should
really be treated as a put
. (As Chapter 5 noted, browsers
don’t all support the HTTP verbs PUT and DELETE, so this input
element is designed to help Rails get
around that, using POST but indicating that it should be treated
differently.)
Rails’ REST capabilities make form_for
seem extra smart, but if you’re not
creating forms explicitly for a RESTful environment, you need to know a
few more things about this method. Underneath, form_for
is pretty much a form-specific
version of the link_to
method shown
in Chapter 2. Like link_to
, it understands Rails’ routing and
will choose its attributes based on that routing.
The form_for
method is part of
ActionView’s FormHelper
module, and the way that Rails’ RESTful scaffolding uses
it relies quite completely on its default behavior. Rails takes @person
as its one clue to what you want and
treats it as a much more complex call to form_for
. The form_for
object can take more
arguments:
Instead of just listing @person
and letting form_for
guess at the structure we
intended, this could have specified :person
as the type, followed by the
@person
object.
The :url
named parameter lets you specify a URL for the
action attribute. It’s unlikely that you’ll just point directly to
a URL, unless it’s one outside of your Rails application. More
typically you’ll ask Rails to create a URL that points to a
controller in your application, something like :url => { :action => "celebrate"
}
.
The scaffolding populated the form
element’s
method, class
, and id
attributes
automatically, but if you
wanted to specify an id
of
special_form
, a class
of my_form
, and a method
of put
, you could specify:
:html => { :id => 'special_form', :class => 'my_form', method => 'put' }
Combined into one, somewhat strange call, this could look like:
<% form_for :person, @person, :url => { :action => "celebrate" }, :html => { :id => 'special_form', :class => 'my_form', method => 'put' } do |f| %>
The form_for
method also sets
up the variable f
, which provides the
context all of the other fields will need to do their work, letting you
use a shorter form to call their helper methods. (You don’t have to call
this variable f
, but it’s a
conveniently short while still memorable-enough name.)
Also, Rails has created an input
element named authenticity_token
, which is based on the session ID. Rails uses this
internally to minimize cross-site request forgery (CSRF) attacks, as
discussed in Chapter 18. This only gets
used for PUT, POST, and DELETE requests—GET requests should all be safe
by design. (If, of course, you designed your application so that GET
requests just return information, not change it.)
If other developers want to script your Rails application from the outside, they certainly can—that’s what the XML side of REST is for.
Finally, you should know that you can create forms in Rails
applications without using form_for
.
You can, of course, create HTML forms by hand. Rails also offers the form_tag
method for creating forms as well as
a set of form field helper methods (also ending in _tag
) if you want to create forms
programmatically, but aren’t binding them directly to a model.