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? %>