Django Comments System – A Complete Guide

Last updated 1 month, 2 weeks ago | 131 views 75     5

Tags:- Python Django

Adding a comment system is a common requirement in many web applications—such as blogs, news portals, forums, and e-learning platforms. While Django does not include a built-in comment framework out of the box anymore (it used to have one in older versions), it’s simple to implement a custom comments system.

This guide will walk you through building a comment system from scratch with models, views, templates, and admin integration.


What Will You Learn?

  • How to define a comment model

  • How to associate comments with posts (or any object)

  • How to create forms for submitting comments

  • How to display comments

  • How to handle nested (threaded) comments (optional)

  • Best practices and common pitfalls


Step 1: Define Models

Let's assume we already have a Post model. We will now create a Comment model to relate to it.

models.py

from django.db import models
from django.utils import timezone

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    created_at = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title


class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    name = models.CharField(max_length=80)
    email = models.EmailField()
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=True)  # Useful for moderation

    def __str__(self):
        return f'Comment by {self.name} on {self.post}'

Run migrations:

python manage.py makemigrations
python manage.py migrate

Step 2: Create a Comment Form

forms.py

from django import forms
from .models import Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['name', 'email', 'body']

Step 3: Create Views to Handle Comments

views.py

from django.shortcuts import render, get_object_or_404, redirect
from .models import Post
from .forms import CommentForm

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    comments = post.comments.filter(active=True)

    new_comment = None
    if request.method == 'POST':
        form = CommentForm(data=request.POST)
        if form.is_valid():
            # Create Comment object without saving to DB yet
            new_comment = form.save(commit=False)
            # Assign the current post to the comment
            new_comment.post = post
            new_comment.save()
            return redirect('post_detail', post_id=post.id)
    else:
        form = CommentForm()

    return render(request, 'blog/post_detail.html', {
        'post': post,
        'comments': comments,
        'form': form,
        'new_comment': new_comment
    })

Step 4: Templates for Display and Submission

post_detail.html

<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>

<h3>Comments ({{ comments.count }})</h3>
{% for comment in comments %}
    <div>
        <p><strong>{{ comment.name }}</strong> ({{ comment.created_at|date:"F d, Y" }})</p>
        <p>{{ comment.body }}</p>
    </div>
{% empty %}
    <p>No comments yet.</p>
{% endfor %}

<h3>Leave a comment</h3>
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Submit Comment</button>
</form>

{% if new_comment %}
    <p>Your comment has been added!</p>
{% endif %}

Add to urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),
]

Admin Configuration

To manage comments via the Django admin:

admin.py

from django.contrib import admin
from .models import Post, Comment

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'post', 'created_at', 'active')
    list_filter = ('active', 'created_at')
    search_fields = ('name', 'email', 'body')

admin.site.register(Post)

Optional: Nested (Threaded) Comments

If you want replies to comments, modify your Comment model:

parent = models.ForeignKey('self', null=True, blank=True, related_name='replies', on_delete=models.CASCADE)

And in templates:

{% for comment in comments %}
    <p>{{ comment.body }}</p>
    {% for reply in comment.replies.all %}
        <div style="margin-left: 30px;">{{ reply.body }}</div>
    {% endfor %}
{% endfor %}

✅ Best Practices

Practice Reason
✅ Use active field To allow moderation of comments
✅ Use related_name Easier reverse querying like post.comments.all()
✅ Sanitize input Automatically handled by Django, but be cautious with rendering
✅ Use pagination for large comment lists For better performance
✅ Validate email field For anti-spam and future notifications

❌ Common Pitfalls

Issue Cause Fix
Comments not saving Form not valid Check for validation errors using form.errors
No comments showing Using wrong related_name or filter Use post.comments.filter(active=True)
XSS Vulnerabilities Rendering raw input Always use {{ comment.body }} instead of `
Flooding or spam No rate limiting Use Django middleware or captcha integration

Summary

Django makes building a comment system easy and extensible. You can expand it with features like:

  • Replies (threaded comments)

  • Email notifications

  • Comment moderation workflows

  • User authentication integration


Complete File Structure Overview

blog/
├── models.py          # Post and Comment models
├── forms.py           # CommentForm
├── views.py           # post_detail view
├── templates/
│   └── blog/
│       └── post_detail.html
├── admin.py           # Admin customization
├── urls.py            # URL routing