Django Creating Comments System to Blog Application
Django Build a blog application with bootstrap and Automatically Generate slugs
Comment Model
Open The models.py file of blog application and below the Post model create the Comment model.
#models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import slugify
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='myapp_post')
updated_on = models.DateTimeField(auto_now= True)
content = models.TextField()
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 Meta:
db_table = "myapp_post"
def save(self, *args, **kwargs):
value = self.title
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
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_on = models.DateTimeField(auto_now_add=True)
active = models.BooleanField(default=False)
class Meta:
ordering = ['created_on']
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
class Meta:
db_table = "myapp_comment"
Database Table
CREATE TABLE myapp_comment (
id INTEGER PRIMARY KEY,
post_id INTEGER,
name STRING (250),
email STRING (200),
body TEXT,
created_on DATETIME,
active STRING (50)
);
we have a Foreign key relation that establishes a many-to-one relationship with the Post model
need to synchronize this comment model into the database by running migrations to reflects the changes in the database.
(django) $ python manage.py makemigrations
(django) $ python manage.py migrate
Adding Comments Model To The Administration Site
Open admins.py file and write the following code.
#admin.py
from django.contrib import admin
from myapp.models import Post, Comment
from django_summernote.admin import SummernoteModelAdmin
#admin.site.register(Post)
class PostAdmin(SummernoteModelAdmin):
list_display = ('title', 'slug', 'status','created_on')
list_filter = ("status",)
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
summernote_fields = ('content',)
admin.site.register(Post, PostAdmin)
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('name', 'body', 'post', 'created_on', 'active')
list_filter = ('active', 'created_on')
search_fields = ('name', 'email', 'body')
actions = ['approve_comments']
def approve_comments(self, request, queryset):
queryset.update(active=True)
Creating forms from models forms.py
#forms.py
from django import forms
from myapp.models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
Building Views views.py
#views.py
from django.shortcuts import render, redirect
from django.views import generic
from myapp.models import Post
from myapp.forms import CommentForm
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
def index(request):
object_list = Post.objects.filter(status=1).order_by('-created_on')
paginator = Paginator(object_list, 3) # 3 posts in each page
page = request.GET.get('page')
try:
post_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
post_list = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
post_list = paginator.page(paginator.num_pages)
return render(request,
'index.html',
{'page': page,
'post_list': post_list})
# class PostDetail(generic.DetailView):
# model = Post
# template_name = 'post_detail.html'
def post_detail(request, slug):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=slug)
comments = post.comments.filter(active=True)
new_comment = None
# Comment posted
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
# Create Comment object but don't save to database yet
new_comment = comment_form.save(commit=False)
# Assign the current post to the comment
new_comment.post = post
# Save the comment to the database
new_comment.save()
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post,
'comments': comments,
'new_comment': new_comment,
'comment_form': comment_form})
Adding URL patterns for Views
from django.contrib import admin
from django.urls import path, include
from myapp import views
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.index),
#path('/', views.PostDetail.as_view(), name='post_detail'),
path('/', views.post_detail, name='post_detail'),
path('summernote/', include('django_summernote.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Templates For The Views post_deatail.html
//templates/post_detail.html
{% extends 'base.html' %}
{% block content %}
<div class="card-body">
<h1>{% block title %} {{ post.title }} {% endblock title %}</h1>
<p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
<p class="card-text ">{{ post.content | safe }}</p>
</div>
<h2>{{ comments.count }} comments</h2>
{% for comment in comments %}
<div class="comments" style="padding: 10px;">
<p class="font-weight-bold">
{{ comment.name }}
<span class=" text-muted font-weight-normal">
{{ comment.created_on }}
</span>
</p>
{{ comment.body | linebreaks }}
<hr/>
</div>
{% endfor %}
<div class="card-body">
{% if new_comment %}
<div class="alert alert-success" role="alert">
Your comment is awaiting moderation
</div>
{% else %}
<h3>Leave a comment</h3>
<form method="post" style="margin-top: 1.3em;">
{{ comment_form.as_p }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% endif %}
</div>
{% endblock %}
