Build the New and Create Functionality from Scratch in Rails
This guide gives a step by step guide for how to implement the new and create functionality in a Rails application.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

This guide gives a step by step guide for how to implement the new and create functionality in a Rails application.

With our index action fully live, let's get to building our new and create actions from scratch.

Rake Routes

First off, let's determine what this looks like from a routing perspective. In the terminal run rake routes. This will show all the available routes for the entire system.

large

As a little bit of a refresher, the Prefix column is our route method. Next, is the verbs, and we'll cover those in-depth later in the course. The next is URI patterns, and these are what we type into the browser to access the view. Anywhere you see :id, it means it is expecting some type of an ID value. The far right column lists the methods which the URLs are mapped to. This format makes it fairly easy to see that.

There is something we can do is to make this list more specific. Imagine, we're in an application that has hundreds of routes. Navigating through them can be quite challenging. When I want to see routes associated with a specific feature, I can use the pipe | in combination with grep and the rake command rake routes | grep <keyword>.

Here we will use rake routes | grep portfolio (The | is the key right above the return key on your keyboard). This command will filter the list and only bring back those routes that have the word "portfolio" in it.

large

You can see, we have a much easier list to read now. It may even highlight the word portfolio—it depends on how your terminal works. Regardless, you will get the same output.

On the first line, if you look at left-most column, you can see the index action portfolios#index. Moving to the left you can see /portfolios , so if you go to "localhost:3000/portfolios", the index method will be triggered.

Implementing our Actions

In this guide, we want to implement our new and create actions. So, the new action is associated with the URL "localhost:3000/portfolios/new". If you type this URL in the browser, it will open a form where you can enter the details/attributes of the portfolio. As discussed in our scaffolds section, this is only the first half of the task as ‘new’ only renders the form. The more important part, where the application actually interacts with the database, is the create action. In fact, this is where all the magic happens in terms of saving the record to the database. Specifically, new is related to the front-end, whereas create is making the system work.

With all that in mind, let's talk about what we need to do in order to build this out.

Open your portfolios_controller.rb and schema.rbin a split screen.

We will take this process one step at a time because if you've never done this before, it can seem a little bit confusing. I want to show you how to build this from square one, so you can really grasp the process.

The first thing we need to do is to create a new method

def new
end

Next, start the rails server and navigate to "localhost:3000/portfolios/new" on your browser. This is going to give an error because we don't have a template in place. This is fine and we could intuit this would happen.

large

If you switch to the terminal, you'll see essentially the same error displayed there as well. It will return a 406 Not Acceptable error . ActionController::UnknownFormat (PortfoliosController#new is missing a template for this request format and variant). It is good to know that the terminal can give us that additional feedback.

The first step to fix this is to go to views/portfolios and create a file called new.html.erb. We'll add a heading to this file.

<h1>Create a new Portfolio Item</h1>

Save your file and refresh the browser page, and you will see it's working.

medium

Creating our Form

In order to get the next piece of this working, we need to have a form. We have an entire section on forms, so I'm not going to duplicate the content and talk about them in depth right now. Let’s go a little rogue and look at the blog form. Open view/blogs/_form.html.erb . You will note this file starts with and underscore _. That indicates it is a partial. The scaffold created this file, and we will delve into the topic of partials later in the course, just keep in mind this represents a partial view element.

For now, just copy the code from that file and paste it into new.html.erb file.

<%= form_for(blog) do |f| %>
  <% if blog.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(blog.errors.count, "error") %> prohibited this blog from being saved:</h2>

      <ul>
      <% blog.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>

  <div class="field">
    <%= f.label :body %>
    <%= f.text_area :body %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Now, we need to make some changes because this in in reference to blogs. First let’s eliminate of all the error code as we're not going to worry about it now.
Your code should now look like this:

<%= form_for(blog) do |f| %>
<div class="field">
    <%= f.label :title %>
    <%= f.text_field :title %>
  </div>

  <div class="field">
    <%= f.label :body %>
    <%= f.text_area :body %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Next we want to make this a form_for for our @portfolio_item, so remove the blog references and replace it with the portfolio items code. Make sure you have an "@" symbol in front of it. Keep the block variable, f. It stands for form, and gets placed in front of each of the attribute items.

- <%= form_for(blog) do |f| %>
+ <%= form_for(@portfolio_item) do |f| %>

In your schema.rb file, look at the portfolio item. As with the blog, we have a title and body however, in between that, we also have a subtitle, so let's insert that.

<div class="field">
    <%= f.label :subtitle %>
    <%= f.text_field :subtitle %>
  </div>

These items are labels and text fields and we will see what they represent when we look at the form in the browser.

If you try refresh the browser now, it will throw an error. I want point this out because it is something that students often run into. The error states that First argument in form cannot contain nil or be empty

You might wonder what that means? form_for is a method, just like our index method or our edit method. The error indicates is that form_for expects an argument, and it can’t be nil or empty. So Ruby and the rails system computed that it needed an @portfolio_item to pass to the form_for method. However it found that nothing like that existed, so it threw an error.

Can you determine why this didn’t work? Ruby thinks it didn't exist is because we need to make this variable available from the controller. This is point where we get into data flow. The controller has a direct line of communication with the view, so we need to provide this instance and make it available inside of the new method.

To make this variable available, go to portfolios_controller.rb and add this line of code @portfolio_item = Portfolio.new to the new method.

  def new
    @portfolio_item = Portfolio.new
  end

If you refresh the browser now, you will see the create a new portfolio item form. It has a title, subtitle and a body. One efficient thing that the form does for us comes from the line that contains f.submit. Even though we didn’t define create portfolio, <%= f.submit %> is a default that creates a submit button for us. So, now we have all of the attributes we need. However, this is not the entire implementation. We have to do a bit more work because new only renders the item, it does not actually create an instance of it.

To test, enter values in the form and click on Create portfolio button. This will throw an error because The action ‘create’ could not be found for PortfoliosController

Open your blogs_controller.rb, and you'll see that our new action is similar to the one in that controller. In both of these actions, we simply created an instance of the item and made it available to the form. That is only half of the process. Next, we have to make the data available and connect to the database to store the values.

Building our Create Action

I'm going to copy the create action from blogs_controller.rb and paste it in portfolios_controller.rb.

  def create
    @blog = Blog.new(blog_params)

    respond_to do |format|
      if @blog.save
        format.html { redirect_to @blog, notice: 'Your post is now live.' }
      else
        format.html { render :new }
      end
    end
  end

Let's walk through exactly what this is going to do. Obviously we don’t want to create blogs, so let’s correct the first line in that method by replacing blog with portfolio_item.

- @blog = Blog.new(blog_params)
+ @portfolio_item = Portfolio.new(blog_params)

Next we want to look at this parameter called blog_params . This next step may be seem a little obscure. We are not going to get into what strong parameters are at this time, as I cover it in detail later in the course. What I am going to do is copy the code inside the blog_params method in the blogs_controller.rb file, params.require(:blog).permit(:title, :body) and paste it into the portfolio's create method. Later on, we will replace this.

@portfolio_item = Portfolio.new(params.require(:blog).permit(:title, :body))

Next we need to update it to reflect our portfolio and it's attributes:

- @portfolio_item = Portfolio.new(params.require(:blog).permit(:title, :body))
+ @portfolio_item = Portfolio.new(params.require(:portfolio).permit(:title, :subtitle, :body))

For now, the important concept to understand, is that we are telling the form what it is allowed to access. This will make more sense later on when we get into forms and security, but for right now, we want to say require a portfolio, and permit a title, subtitle and body. We're not going to worry about thumb images, as we already have them in our seeds file, and we do not need to duplicate those records.

Essentially, this code tells Rails to allow only the parameters we have indicated. In older versions of Rails, this process was not available, so a user could submit a form that didn't have a title, subtitle or body. For example, a hacker could send any information and the database would accept and store it. That was a security flaw that caused a lot of bugs, however, this issue was remedied in later versions. For now, remember, what we are indicating is that these are the specific attributes we are allowing and passing into the database.

In the rest of the code, just replace blog with portfolio_item. The method should then look like this:

def create
    @portfolio_item = Portfolio.new(params.require(:portfolio).permit(:title, :subtitle, :body))

    respond_to do |format|
      if @portfolio_item.save
        format.html { redirect_to portfolios_item, notice: 'Your portfolio item is now live.' }
      else
        format.html { render :new }
      end
    end
  end

Redirecting the Path

I do not want to create the show action yet. Rails is so flexible, you can redirect the method however you want. Go to the blogs/new page on the browser and create a new blog. Once you click the create button, that action directs you to a show page that displays the details of what was just created. We don't have to do the same here . Instead of creating a separate show page, we can redirect to the main portfolios_path page.

- format.html { redirect_to portfolios_item, notice: 'Your portfolio item is now live.' }
+ format.html { redirect_to portfolios_path, notice: 'Your portfolio item is now live.' }

This is getting us a little ahead of ourselves, and I am going to cover this explicitly in the deep dive, but let’s take a quick look at how this works. Go to your terminal and open the routes with the command rake routes

large

Here, we are going to take a look at the prefix. The first one says portfolios. This is a method and it is called prefix because it lists what you would use with path or URL. We will get into the difference between those later. For right now I want to use portfolios_path.

Going back to create method. What we're essentially saying is, if the item is saved successfully, instead of redirecting to the item show page, because we don’t have a show page yet, redirect to the page that displays the full list of portfolio items.

Since that is a different behavior, I wanted to highlight it for you. There are many times when that is the behavior I actually want, as I don't want to send the user to the show page, but would rather send them to the full list of items.

Start the rails server and go to localhost:3000/portfolio/new in your browser. If you try to create a portfolio item, you'll get an error!

How to Address Nil Items

This error is not a bad thing. It says nil is not a valid asset source. We are getting this because of our image tag in our view. It expected a value, but did not get one. If we had submitted an image, then it wouldn't throw this error. There are two ways I could fix this error in development mode.

First, you could comment out that line. Go to your portfolio index page, and comment it out by putting a hashtag # before the equal = sign.

- <%= image_tag portfolio_item.thumb_image %>
+ <%#= image_tag portfolio_item.thumb_image %>

If you save the file and refresh the browser, you'll see that this works. If you scroll down to the very bottom of the page in your browser, you'll also see the new portfolio item was successfully added to the database.

A better way to fix this error is to leave the item there and indicate to use the tag if the thumb_image is not nil

- <%#= image_tag portfolio_item.thumb_image %>
+  <%= image_tag portfolio_item.thumb_image if !portfolio_item.thumb_image.nil? %>

Let’s save and refresh, and you will see that this worked.

With this syntax, I'm asking Rails to run the code
image_tag portfolio_item.thumb_image,
if the !portfolio_item.thumb_image.nil? is true.

Here, nil is a method given by Rails that checks on any item and determines whether it is nil or not. I'm using something of a double negative here. Essentially, I'm saying if portfolio_item.thumb_image is nil, then when I place a bang ! in front of this expression, it gives me the exact opposite of what it is. So if it's true it’s going to be false and if its false it is going to be true.

Now, I think in fact, that is way too confusing. Here is an alternative syntax using unless

<% @portfolio_items.each do |portfolio_item| %>
  <p><%= portfolio_item.title %></p>
  <p><%= portfolio_item.subtitle %></p>
  <p><%= portfolio_item.body %></p>
  <%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
<% end %>

Refresh the browser, and you see that it's still working.

Unless is another method provided by Ruby. This means run image_tag portfolio_item.thumb_image unless the portfolio item is nil. Essentially, we're telling Rails to display the image during every iteration unless that image doesn't exist. That's exactly what it's doing now. Let's add another item, and you'll see that it gets display at the very end of this list, even if there is no image associated with it.

So, all of that is working and we've even implemented a little bit of protection in case a portfolio item doesn’t have a thumbnail image. These types of issues will be taken care of later on when we get to data validations, and we have the ability to add images via the form. Meanwhile, it's good to tool learn how to deal with potentially nil values, as that's a common bug you'll run into while building out Rails applications.

Lastly, let’s head to the terminal and add all these changes to our GitHub branch.

Git status will show us all the files we changed.

Git add . will add all of them.

Git commit -m ‘Integrated new and create actions for portfolios’ will prepare the files and git push origin portfolio-feature will push them to your branch.

Good job on this guide. Though it was a bit longer of an exercise, we learned quite a bit. Hopefully the whole point of the data flow in Rails and how you can pass items between the model view and controller is beginning to make more sense. If not, don't worry as in the next guide we'll do something similar which is to build out the ability to edit items, and this will just add to your undersigning of the MVC process.

Resources