How does form_for work?

No real application is any use unless the user can enter information into it. And in a standard HTML application, or a web-site, the way to do that is through forms.

The HTML form is a simple thing. It consists of the form itself, which when submitted, sends a request (normally a POST, but other HTTP verbs can be used) to an action URL, passing the contents of various inputs as parameters.

Rails tries to map this directly onto your model layer to save you some time and effort. Personally, as I’ve mentioned, I don’t like layering things directly onto my models. And it’s one of those things that’s actually quite easy to get wrong until you know what’s going on.

Let’s look at a typical form:

This is pretty simple. The form_for declaration creates an opening and closing form tag, and everything within the ruby block is enclosed within those tags. The form tag has an ID and a class prefilled for us, based upon the @person we’ve supplied. It also already has an action URL pre-filled; again this depends upon @person.

We call form.label and it magically creates a label tag with the text “First name”. Even more importantly, the label tag has an attribute for=”person_first_name”, so the label and its associated text field are linked.

We call form.text_field and it magically creates an input (type = text) and it takes the current value of first_name from @person and prefills it. It also gives the input an ID of person_first_name (this is important if you have more than one form on a single page, as IDs must be unique) and a name attribute of person[first_name]. This is also important as Rails uses this to unpack the parameters and send them to the right place in your model.

Lastly, form.submit adds an input of type submit, which when clicked, submits the form.

So far, so simple.

But what URL does the form use?

If @person is a brand new object, that’s never been saved to the database before (for example in your controller’s new action), the form uses an HTTP POST to people_path (usually /people). If @person is an existing object that’s been retrieved from the database (for example in your controller’s edit action), the form uses an HTTP PATCH to person_path(@person) (usually /people/123). This is quite neat – we can use the same form in both our new and edit views (sharing them by using a partial). However, it does rely on using Rails’ restful routing conventions.

One thing to note; I often nest my resources. For example, you may be updating a person at a particular company; with a URL of /companies/123/people/456. In which case you need to make sure Rails uses the expected URL.

However, we can no longer share the form between new and edit, as the url is now different for creating new people:

However, we can still use a partial. If we make new.html.erb look like this:

and edit.html.erb look like this:

and copy the internals of the form into _form.html.erb:

then we get the benefits of sharing the form, with the differences extracted out of the way.

One other thing we often need to do is upload a file alongside our models. For the model side of things, I nearly always use the paperclip gem for this as it makes things really really easy. For the view side of things, we need to use a input (type = file) control.

Imagine our person has an avatar associated with them. In paperclip you’d define this as:

Note the security validation to ensure that only images are uploaded here – this is an important security measure to prevent spoofing.

Then we need to add the avatar to our form. This is as easy as changing _form.html.erb to become:

One of the nice things about form_for (as opposed to the raw form_tag method that Rails also offers) is that it sets your form to accept multi-part submissions (that means the form sends two “parts” – the textual form parameters and the binary image file).

So that’s the basics of form_for. There’s much more you can do with Rails forms, including lots of ActiveRecord magic. Personally I like to avoid the magic when I can, but I’ll detail a few of the things you can try later on…

Do you know what to do but not how it works?

Ever wanted to understand why Rails views work the way that they do? Why variables from your controllers are visible inside your views?

Sign up below to get a free 5 part email course on how Ruby on Rails view rendering works and gain a deep understanding of the Rails magic.

We will send you the course, plus the occasional update from this web-site. But no spam, we promise, and it's easy to unsubscribe