Scaffold II - Forms


Collapse Content

Let's look at the code the scaffold generated for modifying posts. This consists of form templates in the view and actions in the controller.

Forms and simple_form

A form lets users submit data to a website. It consists of fields for entering data and a button to submit the data to the server (as an HTTP POST request). Forms allow website users to become active creators of content. You can make a form from scratch with HTML, but Rails makes it easier with form helper methods that generate the HTML for you. The default form helpers are very useful, but they still require a little extra code. In this app, we're using simple_form, which lets you use a very concise syntax to specify the fields of a form.

Here's the ruby code for the posts form:

app/views/posts/_form.html.erb (Hover above code for more info.)

<%= simple_form_for(@post) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :content %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>

With your server running, go to posts/new to view the form that this creates:

In Chrome, right-click on your form and click on "Inspect element". This will let you quickly look at the HTML that was created for the form:

<form ..." id="new_post" action="/posts" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓"><input type="hidden" name="authenticity_token" value="w4xn+...">  

  <div class="form-inputs">
    <div class="form-group text required post_content"><label class="text required control-label" for="post_content"><abbr title="required">*</abbr> Content</label><textarea class="text required form-control" name="post[content]" id="post_content"></textarea></div>
  </div>

  <div class="form-actions">
    <input type="submit" name="commit" value="Create Post" class="btn btn-default">    
  </div>
</form>

There's a lot of code here for a simple form!

At the top, there's a hidden field for an authenticity_token. Rails creates this special token each time a user views a form. This lets Rails know that it's really the user who's submitting a form and not someone else.

The main part of the form contains one field for a post's content:

<textarea class="..." name="post[content]" id="post_content"></textarea>

And then it ends with a button for submitting everything.

Click on the "Network" tab in Chrome's devtools. Now enter some content in your form and click on "Create Post". This should create a a new post on your blog. You can view what information was sent to the server and back in the devtools:

You can also view what information your server received by looking at your logs. Go to your terminal where rails server is running to see what happened when the form was submitted:

Started POST "/posts" for ::1 at 1911-10-04 11:51:40 -0400
Processing by PostsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Oz8+...==", "post"=>{"content"=>"hello"}, "commit"=>"Create Post"}
...
  SQL (0.3ms)  INSERT INTO "posts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "hello"], ["user_id", 7], ["created_at", "1911-10-04 15:51:40.469846"], ["updated_at", "2015-10-04 15:51:40.469846"]]  
  24.6ms)  commit transaction
Redirected to http://localhost:3000/posts/31
Completed 302 Found in 32ms (ActiveRecord: 25.3ms)

You'll often want to examine these logs to debug your forms. (Hover above for more info.) Next, let's see how the controllers handled the information they received.

Controller Code

Here's how a simple update method would look:

def update
  @post.update(params[:post])  #This won't work!
  redirect_to @post, notice: 'Post was successfully updated.'
end

params is provided by Rails to access all parameters that are passed in by a request. params[:post] accesses all the parameters of the post, so passing this to @post.update would update the post with the new data. The next line redirects the browser to display @post's show page.

Question: This code looks simple. Why do we need anything more complicated?
Answer:
Rails prevents this code from working since it's insecure. While we may only display certain HTML fields to the user, a user can send their own HTTP request directly without any form. This will let them pass other parameters into the request, which would let them try to modify attributes that we don't want them to change. For example, let's say their was a user field admin which would give users admin powers when true. A user could submit their own request to a user's update action to change their own admin field to true and thereby grant them unauthorized admin power on the site!

Since we don't know what parameters the user will pass into the request, we can't just update every field that's passed in. You need to specify what parameters can be modified in a given request. This is known as strong params.

Strong params

Rails 4+ uses strong params to control mass assignments of a model's fields. Only fields that are whitelisted can be modified. If you look at the bottom of the posts controller, you'll see the following code:

private 
 #...
 # Never trust parameters from the scary internet, only allow the white list through.
 def post_params
   params.require(:post).permit(:content)
 end

This specifies what fields are required and what fields are permitted. In this case, the :content field is allowed to be modified. The controller actions call this method to get the information from the form in a controlled manner.

def create
  @post = Post.new(post_params)
  ...
end

def update
  respond_to do |format|
    if @post.update(post_params)
      format.html { redirect_to @post, notice: 'Post was successfully updated.' }
      format.json { render :show, status: :ok, location: @post }
    else
      format.html { render :edit }
      format.json { render json: @post.errors, status: :unprocessable_entity }
    end
  end
end

After creating or modifying a post, the controller action specifies what page to return to the user. The default code also includes an option for rendering JSON instead of an HTML page. This can be useful for returning data instead of full HTML page to a front-end or mobile app that will display the data on it's own.

Contact Us
Sign in or email us at [email protected]