Creating a blog is a fantastic way to share your thoughts and ideas with the world. However, a truly engaging blog goes beyond just the articles – it involves building a community around your content. One key feature for fostering this sense of community is the ability for readers to leave comments on your blog posts. In this tutorial, we'll explore how to implement a commenting system in a Django blog.
Prerequisites
- Basic knowledge of Django.
- Django installed on your system.
- A Django project with a blog app.
Step 1: Set Up Your Django Models
First, let's define the models needed for handling comments. In your models.py file, add the following:
from django.db import models
from django.contrib.auth.models import User
from ckeditor.fields import RichTextField
from django.utils.timezone import now
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updated_on = models.DateTimeField(auto_now= True)
content = RichTextField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
class PostComment(models.Model):
sno = models.AutoField(primary_key=True)
comment = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
created_at = models.DateTimeField(default=now)
Make sure to run python manage.py makemigrations and python manage.py migrate to apply these changes to your database.
Step 2: Update Your Blog Views
In your views.py file, you'll need to update the post detail view to handle comments. Add the following imports:
from django.shortcuts import get_object_or_404, redirect
from django.views import generic
from .models import Post, PostComment
from django.views.generic.base import TemplateView
from django.contrib import messages
class PostList(generic.ListView):
queryset = Post.objects.all()
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['recent_posts'] = Post.objects.filter(status=1).order_by('-created_on')[:3]
return context
class PostDetail(generic.DetailView):
model = Post
template_name = 'post_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['recent_posts'] = Post.objects.filter(status=1).order_by('-created_on')[:3]
return context
class ContactView(TemplateView):
template_name = 'contact.html'
class AboutView(TemplateView):
template_name = 'about.html'
class AddComment(generic.DetailView):
def post(self, request, *args, **kwargs):
comment = request.POST.get('comment')
user = request.user
post_id = request.POST.get('post_id')
post = get_object_or_404(Post, id=post_id)
comment_instance = PostComment(comment=comment, user=user, post=post)
comment_instance.save()
messages.success(request, "Comment Added Successfully!!")
return redirect('blog:blog-details', slug=post.slug)
def get(self, request, *args, **kwargs):
messages.error(request, "An error occurred while adding a comment!")
return redirect('blog:home')
for function based views you can use this
def AddComment(request):
if request.method == 'POST':
comment = request.POST.get('comment')
user = request.user
post_id = request.POST.get('post_id')
post = BlogPost.objects.get(id=post_id)
comment_instance = PostComment(comment=comment, user=user, post=post)
comment_instance.save()
messages.success(request, "Comment Added Successfully!!")
else:
messages.error(request, "An error while adding comment!")
return redirect(f'/blog/blog-details/{post.slug}')
Step 3: Update Your Post Detail Template
base.html
<!DOCTYPE html>
<html>
<head>
<title>RS Blog</title>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet">
<meta name="google" content="notranslate" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
</head>
<body>
<style>
body {
font-family: "Roboto", sans-serif;
font-size: 17px;
background-color: #fdfdfd;
}
.shadow {
box-shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.1);
}
.btn-danger {
color: #fff;
background-color: #f00000;
border-color: #dc281e;
}
.hero {
background: black;
height: auto;
padding-bottom: 15px;
box-shadow: 0 16px 48px #E3E7EB;
padding-top: 10px;
}
.position-sticky {
position: sticky;
top: 0px;
z-index: 1000;
}
</style>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light bg-light shadow p-3 position-sticky" id="mainNav">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">RS Blog</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item text-black">
<a class="nav-link text-black font-weight-bold" href="{% url 'home' %}">Home</a>
</li>
<li class="nav-item text-black">
<a class="nav-link text-black font-weight-bold" href="{% url 'about' %}">About</a>
</li>
<li class="nav-item text-black">
<a class="nav-link text-black font-weight-bold" href="{% url 'contact' %}">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}
<!-- Content Goes here -->
{% endblock content %}
<!-- Footer -->
<footer class="py-3 bg-secondary">
<p class="m-0 text-light text-center ">Copyright © RS Blog | <a href="https:rohitashsingh.vercel.app" target="_blank"> Rohitash Singh</a></p>
</footer>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+"
crossorigin="anonymous"></script>
</body>
</html>
index.html
{% extends "base.html" %}
{% block content %}
<style>
body {
font-family: "Roboto", sans-serif;
font-size: 18px;
background-color: #fdfdfd;
}
.head_text {
color: white;
}
.card {
box-shadow: 0 16px 48px #E3E7EB;
}
</style>
<header class="hero">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-12 mx-auto w-100">
<div class="site-heading text-center">
<h3 class=" site-heading my-5 text-white"> Welcome to my awesome Blog </h3>
</p>
</div>
</div>
</div>
</div>
</header>
<div class="container" style="min-height: 61vh;">
<div class="row">
<!-- Blog Entries Column -->
<div class="col-md-8 mt-3 left">
{% for post in post_list %}
<div class="card shadow mb-4">
<div class="card-body">
<h2 class="card-title">{{ post.title }}</h2>
<p class="card-text text-muted h6">{{ post.author }} | {{ post.created_on}} </p>
<p class="card-text">{{post.content|safe|slice:":200" }}...</p>
<a href="{% url 'post_detail' post.slug %}" class="btn btn-dark">Read More →</a>
</div>
</div>
{% endfor %}
</div>
{% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}
</div>
</div>
{%endblock%}
sidebar.html
{% block sidebar %}
<style>
.card {
box-shadow: 0 16px 48px #E3E7EB;
}
a {
text-decoration: none;
}
.position-stic {
position: sticky;
top: 100px;
}
</style>
<div class="col-md-4 float-right">
<div class="card my-4 position-stic">
<h5 class="card-header">Recent Posts</h5>
<div class="card-body">
<ul class="list-unstyled">
{% for post in recent_posts %}
<div class="mb-4">
<a href="{% url 'post_detail' post.slug %}" class="outline-none">
<p class="card-title">{{ post.title }}</p>
</a>
<hr>
</div>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock sidebar %}
post detail template
{% extends 'base.html' %} {% block content %}
<header class="hero">
<div class="overlay"></div>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-12 mx-auto w-100">
<div class="site-heading text-center">
<h3 class=" site-heading my-5 text-white"> {{ post.title }} </h3>
</p>
</div>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-8 card mb-4 mt-3 left top" style="min-height: 80vh;">
<div class="card-body">
<h1>{% block title %} {{ object.title }} {% endblock title %}</h1>
<p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
<p class="card-text ">{{ object.content | safe }}</p>
<hr>
<h2 class="my-4">Comments</h2>
<div class="py-2">
{% for comment in comments %}
<div class="card shadow p-2">
<div class="row">
<div class="col-md-2">{{ comment.user }}</div>
<div class="col-md-10">{{ comment.comment }}</div>
</div>
</div>
{% endfor %}
</div>
<form action="{% url 'AddComment' %}" method="post">
{% csrf_token %}
<input type="hidden" name="post_id" value="{{ post.id }}">
<textarea name="comment" class="form-control" id="comment" rows="5" cols="15"></textarea>
<button type="submit" class="btn btn-primary">Add Comment</button>
</form>
</div>
</div>
{% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}
</div>
</div>
{% endblock content %}
Congratulations! You've just added a commenting system to your Django blog. This feature opens the door for meaningful discussions and interactions within your blog community. Feel free to customize the styling and functionality to suit your blog's unique style and requirements.
Happy coding!
Top comments (1)