Building the Ability to Delete Items in Rails
This guide gives a step by step guide for how to delete items in a Ruby on Rails application. We'll also walk through the differences between delete and destroy in Rails.
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 delete items in a Ruby on Rails application. We'll also walk through the differences between delete and destroy in Rails.

So far, I really like the progress we're making. Our new portfolio module, has links to show pages, it allows us to edit each record and creates new items from scratch. It even has images. One capability that it is missing which we should have is the ability to delete items.

The process of deleting is called destroy in Rails. There is a reason for this, and I want to begin this guide by discussing that.

Start a rails console session with the command rails c. Now, I'm going to take the last record of portfolio and store it in a variable. We can use any variable name here, but I going to go with portfolio. The imperative is portfolio = Portfolio.last Now we have a variable and there are a couple of ways to remove it from the database.

First, we can call the .delete method. So if we simply use the command portfolio.delete that will delete the item.

Since we deleted the last item we pulled, we need to fetch another one. Let's run portfolio_two = Portfolio.last, which is going to have an id of 10 because we deleted the one with an id of 11. Now, we have a variable called portfolio_two. We can use the .destroy method on this one. Use portfolio_two.destroy

large

If you look at the details in the terminal, you can see that both commands used the exact same process. In fact, even the process SQL generated was the same. DELETE FROM “portfolios” WHERE “portfolios”.”id” = $1 Where one item had the id of 11 and the other had the id of 10. So, you may wonder why in the world do we have two processes?

The reason comes down to what the SQL code was wrapped into. If you see the destroy method, you'll see that it has something called a BEGIN and a COMMIT message, whereas in delete, we just have the pure SQL being called. That in an of itself isn’t really a big deal.

For the first time in this course, I'm going to open up the Rails documentation. Let’s start by reading what delete does:

delete()
Deletes the record in the database and freezes this instance to reflect that no changes should be >made (since they can't be persisted). Returns the frozen instance.
The row is simply removed with an SQL DELETE statement on the record's primary key, and no >callbacks are executed.

This line here no callbacks are executed isn’t really going to resonate with you until later on when we cover validations and what callbacks are. Even though you may not appreciate this until later in the course I want to point out that there is a difference and begin to explain why we are using destroy when it seems like kind of an odd word.

Now destroy is different

destroy()
Deletes the record in the database and freezes this instance to reflect that no changes should be made (since they can't be persisted).

There's a series of callbacks associated with destroy. If the before_destroy callback throws :abort the action is cancelled and destroy returns false. See ActiveRecord::Callbacks for further details.

Look at the second line and it says, "there's a series of callbacks associated with destroy."
So what this means is that delete is basically like a pure delete where the process goes into the database and deletes the item completely. The system doesn't really care about validations, callbacks, or anything else surrounding it.

Destroy is a little bit more specific and a little bit more careful when it comes to removing items. For example, if I try to delete an item that some other part of the application is trying to protect, then I would get an error message that'll say something like "I'm sorry, you can't delete this item because it is connected or owned by this other part of the application." Whereas delete would simply go and remove the item.

So, that is the difference between the two methods. I think it's important to know because it's a fairly common question. You’re probably not going to appreciate this difference until much later in the course, but I wanted to give you an intro the differences.

Start the rails server and switch over to sublime.

Creating the Destroy Functionality

Now that we have all the other functionality in place for our portfolio, let's get into the very last thing we want to do, which is to implement the destroy functionality. In our portfolio_controller we need to create the destroy method.

def destroy
end

Destroy is a special word in Rails, and when you run rake routes, you'll see a controller action associated with this word. By default this is going to be included and its very important we keep it here.

One thing to note, destroy does not have its own template. We don't have to create a destroy view file as it just deletes the record and redirects users to a different page. You can even look at the files our scaffold created, and you'll see that there is no page for destroy in /view/blogs.

However, if you go to blogs_controller.rb, which was create by the scaffold, you'll see that we do have a destroy method. I'm going to copy this method and paste it in our portfolios_controller.rb file.

  def destroy
    @blog.destroy
    respond_to do |format|
      format.html { redirect_to blogs_url, notice: 'Post was removed.' }
      format.json { head :no_content }
    end
  end

The first thing we need to note is that our destroy action needs to know which record it is destroying. That makes common sense. If you go to blogs_controller.rb, you'll see that destroy is listed in the before_action callback at the very top of the file. With that in mind let’s take our much used query tool @portfolio_item = Portfolio.find(params[:id]) and place it in our method. Remember, this looks in the parameters and set’s the id then makes that item available as the variable.

def destroy
  @portfolio_item = Portfolio.find(params[:id])

Next, change @blog to @portfolio_item.

- @blog.destroy
+ @portfolio_item.destroy

Notice what we're doing here with @portfolio_item.destroy is exactly what we did in the console. There, I stored the record in a variable and called the .destroy method on it. That is all we are doing here in the controller. I want to reiterate this connection, as it is important to understand this, especially if you're new to Rails. Many students think there is a lot of magic around the Rails processes. With that thought, it can be confusing when you go to build something yourself and you don’t really know what is happening behind the scenes. Conversely if you understand what's going on, then the building process is a lot easier. In general, all the actions you do in the console can also be implemented in the files, so if you spend time in the console looking at these processes such .new or .find or .destroy you will be able to easily implement them in your code files.

So in our portfolio controller when we use @portfolio_item.destroy we are calling destroy on this item. We can even add some comments inside the file to give us a step-by-step guide on what's happening in the destroy method.

def destroy

    # Perform the lookup
    @portfolio_item = Portfolio.find(params[:id])

    # Destroy/delete the record
    @portfolio_item.destroy

    # Redirect
    respond_to do |format|
      format.html { redirect_to blogs_url, notice: 'Post was removed.' }
    end
  end

The third part in destroy method is to redirect the page after a record has been deleted. We just can't leave it at the point where the record has been destroyed. We have to tell Rails what comes next.

You can get rid of the format.json { head :no_content } call as we're not building an API. Once again, this is why I'm not a big fan of scaffolds as it creates a whole lot of code you could potentially want, but that includes a lot of bloat.

So, the respond_to method contains code to instruct the system what it should do after it destroys a record. We want to redirect to the main portfolios page redirect_to portfolios_url,, and have a message that says "Record was removed".

- format.html { redirect_to blogs_url, notice: 'Post was removed.' }
+ format.html { redirect_to portfolios_url, notice: 'Record was removed.' }

The final code is:

  def destroy
    # Perform the lookup
    @portfolio_item = Portfolio.find(params[:id])

    # Destroy/delete the record
    @portfolio_item.destroy

    # Redirect
    respond_to do |format|
      format.html { redirect_to portfolios_url, notice: 'Record was removed.' }
    end
  end

That's pretty straightforward.

Creating a Link

The next part is putting a link for delete in the portfolios page, and that's going to be pretty straightforward too. Go to your blog's index.html.erb, and you'll see how the delete link is implemented.

 <% @blogs.each do |blog| %>
      <tr>
        <td><%= blog.title %></td>
        <td><%= blog.body %></td>
        <td><%= link_to 'Show', blog %></td>
        <td><%= link_to 'Edit', edit_blog_path(blog) %></td>
        <td><%= link_to 'Delete Post', blog, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>

This is the very last line in that block. It uses the link_to method and gives a path. The method method: :delete is the important piece, as we’re passing the delete method and not the destroy method. This is where we tell the system to execute this method. The data attribute is a little bit of Javascript that is going to pop-up a confirmation window that says, "Are you sure you want to do this?"

I'm going to take this code and paste it in our portfolio's index.html.erb file next to each record. Then I will make a couple of changes. We need to change the display to reflect the that we are deleting a portfolio item Delete Portfolio Item, and we need to update the pathportfolio_path(portfolio_item).

- <%= link_to 'Delete Post', blog, method: :delete, data: { confirm: 'Are you sure?' } %>
+ <%= link_to 'Delete Portfolio Item', portfolio_path(portfolio_item), method: :delete, data: { confirm: 'Are you sure?' } %>

I want you to take note of the path in the blog link. It just says blog and not blog_path. This is one of options that Rails gives us to type less code. It is considered syntactic sugar as it is syntax that is designed to make things easier to read or to express. However for our portfolio method, I'm still going to use the full portfolio_path because I want it to match other items. We can always refactor it later to clean up the code.
This is the final code:

<h1>Portfolio Items</h1>

<%= link_to "Create New Item", new_portfolio_url %>

<% @portfolio_items.each do |portfolio_item| %>
  <p><%= link_to portfolio_item.title, portfolio_path(portfolio_item) %></p>
  <p><%= portfolio_item.subtitle %></p>
  <p><%= portfolio_item.body %></p>
  <%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
  <%= link_to "Edit", edit_portfolio_path(portfolio_item) %>
  <%= link_to 'Delete Portfolio Item', portfolio_path(portfolio_item), method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>

Now, you may have noticed that we are using identical code in two of our paths. One to link to a portfolio item and the other to delete an item. That might lead you to wonder how on earth does this work? How will Rails know if it should be sent to the show page or the destroy action?

To answer this question, let’s use our rake routes command in the terminal. We won’t run a specialized query this time because I want to look at the blog routes as well as the portfolio routes.

large

If you look at the destroy action, you can see it doesn't have a method by itself. Whether you are looking at blogs#destroy or portfolios#destroy neither have a prefix. But, the interesting thing is that the prefix carries and cascades down the table.

Looking at the last four items in the table, you can see the the prefix for blog#show is blog. Then, the table doesn't list anymore prefixes. However that same ‘blog’ prefix, or blog_path, is applicable for to both of the update actions and the destroy action as well. The same holds true for portfolio too; portfolio_path is available to the portfolio#show, both Portfolio#update actions, and the portfolio#destroy actions.

The procedure for indicating to Rails which action we actually want to take is in the method listed under the verb column. Look at the portfolios#destroy verb. It says DELETE. That indicates what we should apply in our code so that Rails will know what we are expecting it to do.

Looking back to the code:

  <%= link_to 'Delete Portfolio Item', portfolio_path(portfolio_item), method: :delete, data: { confirm: 'Are you sure?' } %>

you will note, we have our portfolio_path with the portfolio item. The reason this does not redirect to the show page like the path indicates is that it is followed by 'method: :delete`. If we did not include that method, it would simply redirect to the show page.

This is a really important thing to know because it clarifies how data flows inside the application. We have the ability to call the same exact path, but we're letting Rails know ‘that this is the item we want to remove’, and ‘this is the method’ that we are passing to it to use.

Technically, our show page is using the GET verb, and this is where the verbs come into play. By default, Rails assumes that if you don't pass a method in, it'll send it to the GET . This is because a high percentage of paths that we're going to be using are GET messages.

So, we will use that same GET path, but we will have the delete method which will be followed by the data that asks us to confirm the delete action.

Let's test it in the browser now. Start the rails server if you don't already have it running. You should see your portfolio items listed and the delete link by each one.

medium

So, the "Delete Portfolio Item" is a link, and if you click on it, a popup box will ask you to confirm it.

large

If you click ok, the item will be deleted. You can delete a few more items if you like, and it all works perfectly. Great job! We have now implemented the full CRUD functionality, similar to what the scaffolds did. But, since we built it from scratch, hopefully, you have a better understanding of how it all works.

Lastly, let's push the files to our GitHub branch. Remember it is git add . Followed by `git commit -m ‘Implemented destroy action for portfolio feature’. Now we need to send it on git push origin portfolio-feature And that is done.

Also, you can go to PivotalTracker can see we have completed task two. Though it was big task and took a few guides to completed, we have now implemented the full CRUD functionality for our portfolio feature.
In our next guide we will start our discussion of custom routes.

References