Starting from where I left off yesterday. I had manually entered data to the database and fetched the data for viewing. Today, my goal was to take it up a notch and enter data on a form and fetch this data for viewing.
I could have used the resources
available on rails, but because I wanted to create my first posts page from scratch I chose to manually create all the controllers and routes.
1. Building a mental picture
I started off by building a mental picture of what was required. I would need a route with get verbs(get
method) for redirecting to post pages, and I would need a post verb(post
method) for saving the user input data to my database. I would be using the same database Post
that I created yesterday. With a quick Post.delete_all
in my rails console, I wiped my database clean.
Next up, I made the routes ready in route.rb. I would need a /post/index
route to view all the posts from the database (just the titles), posts/new
would route to the form, and posts/:id
would route to a show page that would display the texts of the post in its entirety. I also used an as
command to change the prefix of the of posts#new
to new_post
and posts#show
to post
. These will be very helpful later on. I also changed the homepage for this exercise. My posts/index
would be my homepage. I also would need a post
method to fetch user inputs from the forms.
Rails.application.routes.draw do
get 'posts/index'
root 'posts#index'
post 'posts' => 'posts#create'
get 'posts/new' => 'posts#new' as: 'new_post'
get 'posts/failure' => 'posts#failure` , as: 'failure'
get 'posts/:id' => 'posts#show', as: 'post'
end
TIP: Remember to get
all the methods for posts
before posts/id
else what rails will do is start treating the actions such as new
, failure
as id
, and when such id cannot be found (because they do not exist), rails throws an error.
2. Setting up Controllers
I had already generated the posts controller yesterday via $rails g controller posts
. Therefore, I set forth on making updates to the controller.
First I defined the index function where all the contents of my database would be displayed as per the need. Next I defined the actionsnew
, and show
in the posts
controller as per my route. Finally, I had to set up action for create
.
class PostsController < ApplicationController
def index
@posts = Post.all
end
def show
@post = Post.find(params[:id])
end
def new
@post = Post.new
end
def create
post = Post.new(post_params)
if post.save
redirect_to post_path(post)
else
redirect_to new_post_path
end
end
private
def post_params
params.require(:post).permit(:title,:blob,:author)
end
end
-
Breaking down each action
- The index action as described yesterday gets all posts from the Post database, and stores it in the instance @post , so that it can be later used in the views.
- The show action searches and finds from the database the
:id
. This `param[:id] is generated from the routes. -
the new action (here I am merely guessing) fetches data from the form, and stores it in the instance @post . This data is now ready to be saved to the database. It has not yet been written to the database, however, we have received the data from the form in the new page. So, I did a quick check in my console after I commented the code on the
create
action.Processing by PostsController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"z7vZT7ReRs6LKg5bJuGgnMTuEzYwbDbdDvZmi+HAf2CCNICj2CEm/uxPX6Z WqqEy0GPMLLGLL//Bl+BT/D38kQ==", "post"=>{"title"=>"Introduction", "blob"=>"Hello World. How are you?", "author"=>"Saral"}, "commit"=>"Submit"} No template found for PostsController#create, rendering head :no_content Completed 204 No Content in 56ms (ActiveRecord: 0.0ms)
Let me try and break this down, I can see that the title, blob(or body) and author is being committed to submit. However, then it throws an error saying No templated found for
PostsController#Create
. This gives me an indication that I need to render a template inPostsController#create
. Finally, in the create action is where the magic happens. Here I declared a private function that defined the params for posts as
post_params
(need to understand this better). The params use the method .require and says it needs a post from the database, and only permits input from the title, blob(body) and author from the forms and saves only these into the database. One reason this is used is for security measures. With this in place, SQL injection can be prevented, and malicious users cannot post useless and harmful stuff to the database.
After this is done, the create action fetches the post_params as defined on private. It then checks the condition, if the post was saved successfully it redirects to the post_path. If you recall the post_path is the posts/:id page. Therefore, we get redirected to a pageposts/1
. The '1' is a dummy id in this case. The id will keep changing as the number of items on our database grows. Finally, if the data is unable to be written to our database we redirect to a failure page.
Once all this was done, and my view set up accordingly, I was able to send my data to my view page from the forms.
But, wait how do we set up the views? How do we make a connection between the backend and frontend?
3. The Views
-
Setting up the new page
<div class="container"> <div class = "row"> <div class="col-md-12 post_heading"> <h1> Your blog post goes here </h1> </div> </div> <div class="row"> <div class="col-md-12"> <%= form_for(@post) do |f|%> <div class="form-group"> <%= f.label :title %><br> <%= f.text_field :title , class: "form-control" , placeholder: "Title" , :required => true %> </div> <div class="form-group"> <%= f.label :blob, 'Story' %><br> <%= f.text_area :blob , class: "form-control blog_body" , rows: 20, :required => true %> </div> <div class="form-group text_size"> <%= f.label :author %><br> <%= f.text_field :author , class: "form-control"%> </div> <div id = "btn"> <%= f.submit "Submit", class: "btn btn-primary" %> </div> <% end %> </div>
Noteable observation here is the embedded ruby code for generating the form alongside your regular html/css. We are using form_for
method to get the variable @post
. Recall, how we set up in our controller @post = Post.new
. We are using that very variable to save the title, blod(body) and name of the author. Also, notice how I have used bootstrap classes to the ruby code. The final page looked like this
Finally, as using a similar html view as yesterday's index.html
I was able to fetch and view the data.
<div class="container">
<h1> Post List </h1>
<table class="table">
<thead class="thead-dark">
<tr>
<th scope="col"> Title </th>
</tr>
</thead>
<% @posts.each do |post| %>
<tr>
<td> <%= link_to post.title, post_path(post) %> </td>
<% end %>
</table>
</div>
Final words, there's a lot to unpack from what I did today,but overall I think this post sums it up. I could write a lot more about views and controller,but I feel I will be able to explain things better as I go forward in this coding and writing journey.
Tomorrow, we tacklet updating and deleting posts. Also, if time permits we look into user database and registration.
Top comments (5)
Pretty cool how much you can do in a short period getting started with Rails
True, also the recurring moments of little triumphs are a great feeling. There's a lot going under the hood that I am trying to understand as I go along, but I am also really enjoying the Rails magic. :)
Nice work! Love love love Rails.
One thing to level up your Rails game are partial views -- can you have a header and footer that persists as you navigate across your site (hint: yes).
Good luck!
Rails was my first web framework!
I started off with Flask just to get my hands dirty, but moving to Rails has been really fun. Rails magic is intoxicating.