Edit a Record

Editing and Updating a Post

We can now view a list of all of our posts, read an individual post, and create new ones. Next, it would be nice to be able to make changes to an existing post.

Much like creating a new post, this process will involve two parts:

  1. provide the user with a form to allow editing of the existing post
  2. receive that submitted form data and update the appropriate post in the database

So first let's define a route to show a form so that we can edit the post:

### config/routes.rb ###

Rails.application.routes.draw do
  # ...
  get  '/edit_post/:id' => 'application#edit_post'
end

An action to render our edit form:

### app/controllers/application_controller.rb ###

class ApplicationController < ActionController::Base

  # ...

  def edit_post
    post = connection.execute("SELECT * FROM posts WHERE posts.id = ? LIMIT 1", params['id']).first

    render 'application/edit_post', locals: { post: post }
  end

  # ...
end

And a view for our edit form:

<!-- app/views/application/edit_post.html.erb -->

<html>
  <body>

    <form method="post" action="/update_post/<%= post['id'] %>">

      <label for="title"> Title</label>
      <input id="title" name="title" type="text" value="<%= post['title'] %>"/>
      <br /> <br />

      <label for="body"> Body</label>
      <textarea id="body" name="body"><%= post['body'] %></textarea>
      <br /> <br />

      <label for="author"> Author</label>
      <input id="author" name="author" type="text" value="<%= post['author'] %>"/>
      <br /> <br />

      <input type="submit" value="Update Post" />

    </form>

    <br />
    <a href="/list_posts">Back to Posts</a>

  </body>
</html>

Our route simply sends requests in the format of /edit_post/:id to application#edit_post, capturing the post ID just like we did in the show_post route.

Inside the edit_post action, we find the post to edit using the same call to the database as we used in show_post, and then pass it into the application/edit_post view.

This edit_post view is identical to the new_post with three exceptions:

  1. our submit button reads Update Post
  2. all of the <form> fields are populated with the values of the post
  3. the <form> action attribute points to our new route (using the proper post ID)

Updating a Post

With our edit form now in place, we'll need to define the route and action to actually update the post in the database.

The <form> action above points to a path of the format /update_post/:id, so we'll need to define that route:

### config/routes.rb ###

Rails.application.routes.draw do
  # ...
  post '/update_post/:id' => 'application#update_post'
end

And then we'll define this update_post action:

### app/controllers/application_controller.rb ###

class ApplicationController < ActionController::Base

  # ...

  def update_post
    update_query = <<-SQL
      UPDATE posts
      SET title      = ?,
          body       = ?,
          author     = ?
      WHERE posts.id = ?
    SQL
    connection.execute update_query, params['title'], params['body'], params['author'], params['id']

    redirect_to '/list_posts'
  end
end

The <form> above will make a POST request to /update_post/:id, so we define our route with post and direct requests of that format to a new action, application#update_post.

Inside this new action, we execute a SQL statement to UPDATE the post by its ID, using the new values submitted by the application/edit_post <form>.

Remember, the name attributes on our <input>s in the application/edit_post view determine what these values are called in params when they arrive in our controller action.

After the post is updated in the database, then we redirect_to '/list_posts'.

So try it out! If you visit /edit_post/1, change something, and submit the form, you'll be able to make changes to that post.

Link to Edit Post from List Posts

Editing posts works now, but we don't have any links to get to the edit post page yet. Let's change that by adding an Edit link next to each post title in list_posts:

<!-- app/views/application/list_posts.html.erb -->

<html>
  <body>

    <div class="posts">
      <% posts.each do |post| %>
        <div class="post">

          <h2 class="title"> <!-- ... --> </h2>

          <small class="meta">

            <!-- ... -->

          </small>
          <!-- link to edit post -->
          <a href="/edit_post/<%= post['id'] %>">Edit</a>
        </div>
        <hr />
      <% end %>
    </div>

    <!-- ... -->

  </body>
</html>

This whole post editing process is analogous to the post creation process. That is, edit_post behaves very similarly to new_post (provides the user with a <form>), and update_post behaves very similarly to create_post (executes SQL and redirects). The key differences are that the edit_post route requires a post ID (since editing, unlike creating a post, is in reference to an existing database row) and the edit_post view needs to populate its <form> with the values of the post it represents.

Redirect vs. Render

You probably have seen a bit of a pattern here: whenever we do HTTP POST, we redirect instead of render. This is because of HTTP semantics. That is, the meaning of a POST request is different from that of a GET request.
When we POST, we're requesting for the server to do something e.g. change something in the database. With a GET request, we're asking the server to give us something. So the point of a POST request is not to get something, but to have the server do something. But, even though the POST request itself is not a request for a document (e.g. HTML), we're still making these POST requests from a browser, so we have to show the user something. And so, once our action is done successfully processing a POST request, it delegates responsibility for displaying something to the user on to another action, by redirecting. Another reason for redirecting instead of rendering is that if we render after a POST request, we may run into errors when trying to refresh the page as the URL from the POST request will not be changed, even though a new page has been rendered.