Topic: Revealing the Magic Behind Forms

Forms in Rails are mysterious. Add a few columns to a table, add a few fields to a form and BAM! You have yourself a functional app. The fields map to the database like magic. In this article I will take you behind the scenes so you can see exactly what Rails is doing.

In this example we have a User model with name and email attributes. We have a form for creating a User model:

# in new.rhtml
<% form_tag :action => 'create' do %>
  Name: <%= text_field :user, :name %>
  Email: <%= text_field :user, :email %>
  <%= submit_tag 'Create' %>
<% end %>

To understand this fully we need to look at the HTML which is generated by this code.

<form action="/users/create" method="post">
  Name: <input id="user_name" name="user[name]" size="30" type="text" />
  Email: <input id="user_email" name="user[email]" size="30" type="text" />
  <input name="commit" type="submit" value="Create" />
</form>

Everything looks pretty normal except for one thing: the name of the text fields. What's with those square brackets in "user[name]" and "user[email]"? This is part of the magic. Rails uses these square brackets to group all of the fields belonging to the User model.

When the form is submitted these text fields (with their wacky names) are sent to the server. Rails interprets these submitted values and puts them in a "params" hash which you then access in the controller. Take a look at the server log when this form is submitted to see how Rails groups the values.

Parameters: {"user"=>{"name"=>"Joe", "email"=>"joe@example.com"}, "commit"=>"Create", "action"=>"create", "controller"=>"users"}

Rails is making a mini hash inside of this params hash. In other words, it is grouping all the user fields into its own section so you can easily fetch the User specific values.

This is only half of the story. Now take a look at the "create" action in the controller.

def create
  @user = User.new(params[:user])
  if @user.save
    redirect_to :action => 'index'
  else
    render :action => 'new'
  end
end

Here we are creating a new user (User.new) and passing "params[:user]" to this method. If you look back at the parameters in the log you can see that "user" is the name of the mini hash where the values are grouped. Here's what we're actually passing into the "new" method:

@user = User.new(:name => 'Joe', :email => 'joe@example.com')

If you are familiar with models and ActiveRecord you probably know that you can pass a hash when creating a model to set multiple attributes - exactly as we are doing here. When we save the model it all goes into the database. That is, unless there's a validation error.

When there's a validation error, the "save" method returns false so the "else" condition is executed. As you can see, this renders the form again. But, how do the values get back in the form fields?

Form helpers like "text_field" have a special behavior. They take the first parameter (the name of the model, in this case "user") and look for an instance variable with that same name (@user). It will then look up the attributes in this @user model and display them in the field.

The way Rails uses square brackets in the form field names may be a little strange at first, but it allows you to do some extremely powerful things with very little effort. See my other tutorials on handling multiple models in forms for examples of this.

Railscasts - Free Ruby on Rails Screencasts

Re: Revealing the Magic Behind Forms

Awesome for a beginner like me! Just what I have been searching for. We need more texts on basic understanding. Most texts seems to focus on just getting things done which isn't really that useful when you're about to do something by your self.

Last edited by Robin (2007-03-26 11:51:51)

Re: Revealing the Magic Behind Forms

Thanks for tutorial!
Even more mysterious stuff happens with scaffolded datetime stuff big_smile

iChat ahoge_@mac.com

Re: Revealing the Magic Behind Forms

Thanks Ryanb,
I was actually going in rounds with the forms when I stumbled into your tutorials. It's really well written.
But it would be nice if you could add these also.
params[:user] returns the id of the user. so it can only be used to create and update a user. If you want to specifically access, say name you may have to use
params[:user][:name]. (Actually I had a hard time finding it out.)
eg: User.find(:first, :conditions => ["login = ? and password = ?",               
                                  params[:user][:login],params[:user][:password]])
Thanks
Chris

Last edited by chryste (2007-06-27 05:26:22)

Re: Revealing the Magic Behind Forms

The params[:user] should return a hash of the values in there. Like this:

params[:user] # => { :name => "foo", :email => "bar", ... }

This is mainly used when creating or updating a user model. If all you're doing is searching for a user model using those fields then you should use text_field_tag and related form helpers.

<%= text_field_tag :name %>

That way you can extract it like this:

params[:name]

It's not grouped in a "user" hash then. See this other tutorial for details.

Railscasts - Free Ruby on Rails Screencasts

Re: Revealing the Magic Behind Forms

Thanks Ryanb,
That wad what I was saying, if it's for creating a user, you'll get all those fields, but not while searching.

Nice tutorials,. Keep up the good work.
thanks
Chris.

Re: Revealing the Magic Behind Forms

Very nice tutorial. It's good clearing up Rails' magicness, Rails can be very helpful in ways like this, but at the same time it can cause confusion and the sense of not knowing whats going on.

Having tutorials like this is almost identical to revealing a magic trick.

I just wish I read this while I was a beginner. Thanks anyway!

Sam