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:
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:
Update Post
<form>
fields are populated with the values of the post
<form>
action
attribute points to our new route (using the proper post ID)
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.
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.