Post
Generate Files for Post.
   $ rails g scaffold Post
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
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
Create Post table by running:
   $ rails db:migrate
Terminal output:
   == 20211010101809 CreatePosts: migrating ======================================
   -- create_table(:posts)
      -> 0.0032s
   == 20211010101809 CreatePosts: migrated (0.0040s) =============================
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 %>
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
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 %>
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 %>
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
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
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
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
Terminal output:
 == 20211017054528 CreateComments: migrating ===================================
 -- create_table(:comments)
    -> 0.0215s
 == 20211017054528 CreateComments: migrated (0.0218s) ==========================
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
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 %>
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
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 %>
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
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
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 %>
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)
models/comment.rb
class Post < ApplicationRecord
belongs_to :post
end
is this a valid one ?
It should be:
class Comment< ApplicationRecord
belongs_to :post
end
Thank you for noticing! I made the update already.
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?
You probably don't have a post_id for comment table
I'm getting this error:
ActionController::ParameterMissing (param is missing or the value is empty: post):can anyone help me ???
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,
Hello sir, I am getting this error message?
undefined method `errors' for nil:NilClass
<% if comment.errors.any? %>