DEV Community

John Kevin Baluyot
John Kevin Baluyot

Posted on • Updated on

Create Post and Comment in Ruby on Rails

Post

Generate Files for Post.

   $ rails g scaffold Post
Enter fullscreen mode Exit fullscreen mode

Ruby will create several files:

   create    db/migrate/20211010101809_create_posts.rb
   create    app/models/post.rb
   invoke  resource_route
   route    resources :posts
   invoke  scaffold_controller
   create    app/controllers/posts_controller.rb
   create      app/views/posts
   create      app/views/posts/index.html.erb
   create      app/views/posts/edit.html.erb
   create      app/views/posts/show.html.erb
   create      app/views/posts/new.html.erb
   create      app/views/posts/_form.html.erb
Enter fullscreen mode Exit fullscreen mode

Add fields to Post by editing the newly created migration file.

   class CreatePosts < ActiveRecord::Migration[6.1]
     def change
       create_table :posts do |t|
         t.string :title
         t.text :text

         t.timestamps
       end
     end
   end
Enter fullscreen mode Exit fullscreen mode

Create Post table by running:

   $ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Terminal output:

   == 20211010101809 CreatePosts: migrating ======================================
   -- create_table(:posts)
      -> 0.0032s
   == 20211010101809 CreatePosts: migrated (0.0040s) =============================
Enter fullscreen mode Exit fullscreen mode

This means that the Post table was successfully created.

Update app/views/posts/_form.html.erb.

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

         <ul>
           <% post.errors.each do |error| %>
             <li><%= error.full_message %></li>
           <% end %>
         </ul>
       </div>
     <% end %>

     <%= form.label :title, "Title:" %>
     <%= form.text_field :title %>

     <%= form.label :text, "Text:" %>
     <%= form.text_area :text %>

     <div class="actions">
       <%= form.submit %>
     </div>
   <% end %>
Enter fullscreen mode Exit fullscreen mode

This form will be used to create or update posts.

Go to app/controllers/posts_controller.rb.
Add fields to the strong params to the controller for it to accept values from the app/views/posts/_form.html.erb. This is done for added security.

   class PostsController < ApplicationController
       #---------
       # Other actions
       #---------

       def post_params
         params.require(:post).permit(:title, :text)
       end
   end
Enter fullscreen mode Exit fullscreen mode

We're almost done with the post. Now we're just gonna edit the views to properly show the data saved in the post.

Edit app/views/posts/index.html.erb.

   <p id="notice"><%= notice %></p>

   <h1>Posts</h1>

   <table>
     <thead>
        <tr>
         <th colspan="3"></th>
       </tr>
     </thead>

     <tbody>
       <% @posts.each do |post| %>
         <tr>
           <td><%= post.title %></td>
           <td><%= link_to 'Show', post %></td>
           <td><%= link_to 'Edit', edit_post_path(post) %></td>
           <td><%= link_to 'Destroy', post, method: :delete, data: { confirm: 'Are you sure?' } %></td>
         </tr>
       <% end %>
      </tbody>
   </table>

   <br>

   <%= link_to 'New Post', new_post_path %>
Enter fullscreen mode Exit fullscreen mode

This will show the post's title in "localhost:3000/posts".

   <! -- app/views/posts/show.html.erb -- >
   <p id="notice"><%= notice %></p>

   <%= @post.title %>
   <br>
   <%= @post.text %>
   <br>

   <%= link_to 'Edit', edit_post_path(@post) %> |
   <%= link_to 'Back', posts_path %>
Enter fullscreen mode Exit fullscreen mode

This will show post title and text values at the "localhost:3000/posts/[id]".

We're done at the first half. Now we'll begin at Comment.

Comment

Generate files for Comment.

 $ rails g scaffold Comment
Enter fullscreen mode Exit fullscreen mode

Ruby will create some files:

 create    db/migrate/20211017054528_create_comments.rb
 create    app/models/comment.rb
 invoke  scaffold_controller
 create    app/controllers/comments_controller.rb
 invoke    erb
 create      app/views/comments
 create      app/views/comments/index.html.erb
 create      app/views/comments/edit.html.erb
 create      app/views/comments/show.html.erb
 create      app/views/comments/new.html.erb
 create      app/views/comments/_form.html.erb
Enter fullscreen mode Exit fullscreen mode

Comments are usually rendered under Post so we will be using only _form.html.erb.

Add fields to Comment by editing the newly created migration file.

 class CreateComments < ActiveRecord::Migration[6.1]
   def change
     create_table :comments do |t|
       t.integer :post_id
       t.text :text

       t.timestamps
     end
   end
 end
Enter fullscreen mode Exit fullscreen mode

If you noticed, I added the post_id field to the comment. This will be used as a reference to the Post
table.

Create the Comment table by running:

 $ rails db:migrate
Enter fullscreen mode Exit fullscreen mode

Terminal output:

 == 20211017054528 CreateComments: migrating ===================================
 -- create_table(:comments)
    -> 0.0215s
 == 20211017054528 CreateComments: migrated (0.0218s) ==========================
Enter fullscreen mode Exit fullscreen mode

This means that the Comment table was successfully created.

Now we will be adding the relationship between Post and Comment.

 #models/post.rb
 class Post < ApplicationRecord
  has_many :comments
 end

 #models/comment.rb

 class Comment < ApplicationRecord
  belongs_to :post
 end
Enter fullscreen mode Exit fullscreen mode

Update app/views/comments/_form.html.erb.

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

     <ul>
      <% comment.errors.each do |error| %>
       <li><%= error.full_message %></li>
      <% end %>
     </ul>
    </div>
   <% end %>

   <%= form.hidden_field :post_id %>

   <%= form.label :text, "Text:" %>
   <%= form.text_area :text %>

   <div class="actions">
    <%= form.submit %>
   </div>
  <% end %>
Enter fullscreen mode Exit fullscreen mode

We're getting the comment form to be added under the post.

Update strong params to accept values from comment form at the comments_controller.rb.

 class CommentsController < ApplicationController
    #---------
    # Other actions
    #---------

    def comment_params
     params.require(:comment).permit(:post_id, :text)
    end
 end
Enter fullscreen mode Exit fullscreen mode

Add comment form in app/views/posts/_form.html.erb.

 <p id="notice"><%= notice %></p>

 <%= @post.title %>
 <br>
 <%= @post.text %>
 <br>

 <%= render "comments/form", comment: @comment%>

 <%= link_to 'Edit', edit_post_path(@post) %> |
 <%= link_to 'Back', posts_path %>
Enter fullscreen mode Exit fullscreen mode

This may not work yet. We need to @comment variable used for the comment form.

Update post_controller.rb

 class PostsController < ApplicationController
    #---------
    # Other actions
    #---------

    def show
     @comment = @post.comments.build
    end

   #---------
   # Other actions
   #---------

 end
Enter fullscreen mode Exit fullscreen mode

This will create @comment each time you show a post. The comment form under the post should work now but if the form has been submitted, the page redirects to "localhost:3000/comments/[id]" instead of "localhost:3000/posts/[id]".

To redirect to "localhost:3000/posts/[id] after submitting a comment we have to update comments_controller.rb.

 class CommentsController < ApplicationController
    #---------
    # Other actions
    #---------

    def create
     @comment = Comment.new(comment_params)

     respond_to do |format|
       if @comment.save
         format.html { redirect_to @comment.post, notice: "Comment was successfully created." }
       else
         format.html { render :new, status: :unprocessable_entity }
       end
     end
   end

   #---------
   # Other actions
   #---------
 end
Enter fullscreen mode Exit fullscreen mode

We're almost done. Now we only have to render the comments under the post.

Edit app/views/posts/show.html.erb.

 <p id="notice"><%= notice %></p>

 <%= @post.title %>
 <br>
 <%= @post.text %>
 <br>

 <b>Comments</b>
 <br>
 <%- @post.comments.each do |comment|%>
   <%= comment.text%>
   <br>
 <% end %>

 <%= render "comments/form", comment: @comment%>

 <%= link_to 'Edit', edit_post_path(@post) %> |
 <%= link_to 'Back', posts_path %>
Enter fullscreen mode Exit fullscreen mode

To check posts go to 'localhost:3000/posts'

That's it we're done šŸŽ‰

If you want to see the code, you could check here:
Github repository


Update 1/30/2022

I created a new blog post that adds real-time loading of comments for this, which can be seen here: Ruby on Rails - Comment real-time loading

Update 6/18/2022

Updated based on the comment

Top comments (7)

Collapse
 
mukeshmahara profile image
mukesh mahara

models/comment.rb

class Post < ApplicationRecord
belongs_to :post
end

is this a valid one ?

Collapse
 
jkevinbaluyot profile image
John Kevin Baluyot

It should be:
class Comment< ApplicationRecord
belongs_to :post
end

Thank you for noticing! I made the update already.

Collapse
 
skytreesea profile image
Kim Changhyun • Edited

Thank you for the kind post, which is absolutely helpful.
I am following your post and have got an error.
Do you have any clue about this?

unknown attribute 'post_id' for Comment.
def show
@comment = @post.comments.build
end

Collapse
 
jkevinbaluyot profile image
John Kevin Baluyot

You probably don't have a post_id for comment table

Collapse
 
usermatth profile image
Matheus Cardoso

I'm getting this error:

ActionController::ParameterMissing (param is missing or the value is empty: post):

can anyone help me ???

Collapse
 
jkevinbaluyot profile image
John Kevin Baluyot

That happens to me when I submit a form without value on it. I didn't put a javascript that checks the value of forms in this article,

Collapse
 
bob_student profile image
Bob Student

Hello sir, I am getting this error message?
undefined method `errors' for nil:NilClass
<% if comment.errors.any? %>