Rails 5 Basics : View to Model

Objective

  • Learn how to get data from the user and save it in the database

Steps

Step 1

We need to display a form for the user to fill out the text field for the article title and text area for the description. In order for the user to go to this form, let's create a 'New Article' link to load an empty form in the articles index page.

Add the following code to the bottom of the app/views/articles/index.html file:

<%= link_to 'New Article', ? %>

Step 2

What is the url helper we should use? We know we need to display the articles/new.html.erb page. We also know that the action that is executed is 'new' before new.html.erb is displayed. Take a look at the rake routes output.

The first column named Prefix gives us the URL helper we can use. We can either append 'url' or 'path' to the prefix. Let's fill in the url helper to load the new page as follows:

<%= link_to 'New Article', new_article_path %>

Step 3

Reload the page http://localhost:3000/articles in the browser. The hyperlink for creating a new article will now be displayed.

Step 4

Right click on the browser and click 'View Page Source'. You will see 'New Article' link pointing to the resource /articles/new.

<a href="/articles/new">New Article</a>

Step 5

Click the 'New Article' link. Go to the terminal and look at the server output.

Started GET "/articles/new" for ::1 at 2015-11-28 14:20:54 -0800

AbstractController::ActionNotFound (The action 'new' could not be found for ArticlesController):

You can see that the browser made a http GET request for the resource /articles/new. You will see the unknown action error page.

Step 6

Let's create the new action in articles controller. Add the following code to articles controller:

def new

end

Step 7

Reload the browser http://localhost:3000/articles/new page. In the log, you will see:

Started GET "/articles/new" for ::1 at 2015-11-28 14:22:55 -0800
Processing by ArticlesController#new as HTML
No template found for ArticlesController#new, rendering head :no_content
Completed 204 No Content in 4ms (ActiveRecord: 0.0ms)

After the new action is executed Rails looks for view whose name is the same as the action, in this case app/views/articles/new.html.erb.

Step 8

So lets create new.html.erb under app/views/articles directory with the following content:

<%= form_for @article do |f| %>
  <p>
    <%= f.label :title %><br>
    <%= f.text_field :title %>
  </p>

  <p>
    <%= f.label :description %><br>
    <%= f.text_area :description %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

Step 9

Click on the 'New Article' link on the 'Listing Articles' page.

ActionView::Template::Error (First argument in form cannot contain nil or be empty):
    1: <%= form_for @article do |f| %>
    2:   <p>
    3:     <%= f.label :title %><br>
    4:     <%= f.text_field :title %>
  app/views/articles/new.html.erb:

You will now see the above error.

Step 10

Change the new method in articles controller as follows:

def new
  @article = Article.new
end

Here we are instantiating an instance of Article class, this gives Rails a clue that the form fields is for Article model.

Step 11

Reload the browser http://localhost:3000/articles/newpage. You will now see an empty form to create a new article.

Step 12

Right click and select 'View Page Source' on the new article form page.

<form class="new_article" id="new_article" action="/articles" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" />

As you can see, form will be submitted to the url '/articles' and the http verb used is POST. When the user submits the form, which controller and which action will be executed?

Step 13

Look at the output of rake routes, the combination of the http verb and the URL uniquely identifies the resource end point.

POST   /articles(.:format)          articles#create

In this case we see that it maps to the articles controller and create action.

Step 14

Fill out the form and click 'Create Article'. Check the server log output.

Started POST "/articles" for ::1 at 2015-11-28 14:30:23 -0800

AbstractController::ActionNotFound (The action 'create' could not be found for ArticlesController):

You can see that the browser made a post to the URL '/articles'. This error is due to absence of the create action in the articles controller.

Step 15

Define the create method in the articles controller as follows:

def create

end

Step 16

Fill out the form and click 'Create Article'. You can see that the form values submitted by the user is sent to the server. Rails automatically populates a hash called params which contains a key whose name is the article symbol and the values are the different database columns and its values.

Started POST "/articles" for ::1 at 2015-11-28 14:31:43 -0800
Processing by ArticlesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"WMLaod3Y28D", "article"=>{"title"=>"How to get rick quick", "description"=>"It's very easy. Borrow lot of money and build lot of houses. If Donald Trump can do it, you can do it!"}, "commit"=>"Create Article"}
No template found for ArticlesController#create, rendering head :no_content
Completed 204 No Content in 3ms (ActiveRecord: 0.0ms)

You will see No template found in the log file.

Step 17

Before we fix the missing template issue, we need to save the data submitted by the user in the database. You already know how to use the ActiveRecord class method 'create' to save a record. You also know how Rails populates the params hash, this hash is made available to you in the controller. So we can access it like this :

def create
  Article.create(params[:article])
end

In Figure 50, you see that the hash key article is a string, but I am using the symbol :article in the create method. How does this work?

As you can see from the rails console, params hash is not a regular Ruby hash, it is a special hash called HashWithIndifferentAccess. It allows you to set the value of the hash with either a symbol or string and retreive the value using either string or symbol.

Step 18

Fill out the form and submit again. Now we get a forbidden attributes error.

Step 19

Due to security reasons we need to specify which fields must be permitted as part of the form submission. Modify the create method as follows:

def create
  Article.create(params.require(:article).permit(:title, :description))
end

Step 20

Fill out the form and submit again. You will get the template missing error but you will now see that the user submitted data has been saved to the database.

Started POST "/articles" for ::1 at 2015-11-28 14:34:42 -0800
Processing by ArticlesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"BR6kUTeE8yVh", "article"=>{"title"=>"adfsda", "description"=>"afdadsfa"}, "commit"=>"Create Article"}
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "articles" ("title", "description", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["title", "adfsda"], ["description", "afdadsfa"], ["created_at", 2015-11-28 22:34:42 UTC], ["updated_at", 2015-11-28 22:34:42 UTC]]
   (49.3ms)  commit transaction
No template found for ArticlesController#create, rendering head :no_content
Completed 204 No Content in 66ms (ActiveRecord: 51.0ms)
$rails c
Loading development environment (Rails 5.0.0.alpha)
 > Article.last
  Article Load (0.1ms)  SELECT  "articles".* FROM "articles" ORDER BY "articles"."id" DESC LIMIT 1
 => #<Article id: 3, title: "test", description: "some description", created_at: "2015-11-28 22:34:42", updated_at: "2015-11-28 22:34:42"> 

The ActiveRecord class method 'last' method retrieves the last row in the articles table.

Step 21

Let's now address the missing template error. Once the data is saved in the database we can either display the index page or the show page for the article. Let's redirect the user to the index page. We will be able to see all the records including the new record that we created. Modify the create method as follows:

def create
  Article.create(params.require(:article).permit(:title, :description))

  redirect_to articles_index_path
end

How do we know that we need to use articles_index_path url helper? We already saw how to find the URL helper to use in the view, we can do the same. If you see the output of rake routes command, we know the resource end point, to find the URL helper we look at the Prefix column.

Step 22

Fill out the form and submit again. You will now see all the articles displayed in the index page.

Summary

We saw how we can save the user submitted data in the database. We went from the View to the Controller to the Model. We also saw how the controller decides which view to render next. We learned about the http verb and identifying resource endpoint in our application. Now we know how the new and create action works. In the next lesson we will see how edit and update action works to make changes to an existing record in the database.


Related Articles


Create your own user feedback survey