how form_for works in Ruby on Rails

Ivan Wang picture Ivan Wang · Dec 10, 2013 · Viewed 13k times · Source

I am an newbie. I have read the API documentation. But still don't understand how form_for works.

Firstly, from Ruby on Rails Tutorial, the form for follow button:

<%= form_for(current_user.relationships.build(followed_id: @user.id)) do |f| %>
  <div><%= f.hidden_field :followed_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>

I understand current_user.relationships.build(followed_id: @user.id) means a new record. But why can we not just submit and trigger controller to save the record without hidden_field? Why do we still need to post followed_id to controller?

Secondly, in hidden_field, what does :followed_id means? I believe that is a symbol, i.e. it equals only "followed_id" not a variable of id. If that is only the name of the input field, then what is its value?

Thirdly, how does form_for know where the submission should be sent to? Which controller and action the form_for will post to?

Fourth, how does params work with form_for? In this follow button case, params[:relationship][:followed_id] will return @user.id in controller. How does it know the first hash attribute is :relationship? We have neither mentioned form_for :relationship nor form_for @relationship.

I know these questions can be very dumb, but I am really stuck. Any help will be appreciated.

Answer

Aurelien Schlumberger picture Aurelien Schlumberger · Dec 10, 2013

I didnt do that tutorial so mind me if i dont answer directly to your question.

Take a look at the rails guide about form helpers and it explains in details your questions, probably in a more articulate way than i can.

form_for(path/to/your/controller/action) is a helper method to create HTML form elements with the url path to the POST or GET request. The helper knows if it should be a new record or an update record based on what you are asking to do in your controller action.

For example In your controller

def new
  @my_instance_variable = Myobject.new
end

In your view new.html.erb

<%= form_for @my_instance_variable do |f| %>
...
<% end %>

In your case the logic was directly written in the helper and you could also directly write

<%= form_for Myobject.new %>

Both will result with the following html

<form action="/myobjects/new" method="post">
# in this case rails knows its a `POST` request because the route new action
# is by default a POST request. You can check these routes and their request 
# by using `rake routes` in terminal.

Then the hidden_field is another helper to contain a value, in your case the @user.id that will be passed as parameter then saved as a Create or update action for the given object. The reason it doesnt add the value in the hidden field tag is because you already have a model association that knows the id of user since the link of form uses the build method with user id.

Last part you need to understand the form_for link logic

current_user.relationships 
# implies the association of the current_user has many relationships 
current_user.relationships.build 
# .build is a method to populate a new object that can be save as a new record
# means you will create a new relationship record by populating the user_id 
# column with the current_user.id and the followed_id with the target @user.id