How To Create A Comment Section For Your Django Blog!
Maybe your like
Hi!
This mini-tutorial will focus on creating a Comment Section for adding (without update/delete) comments to each blog post. We will implement this feature while using a class-based view, namely our BlogPost DetailView, mine's looks like this:
# MainApp/models.py class BlogPost(models.Model): title = models.CharField(max_length=100) subtitle = models.CharField(max_length=200, blank=True, null=True) content = models.TextField() date_posted = models.DateTimeField(default=timezone.now) author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) image = models.ImageField(upload_to='blog_images', storage=gd_storage, null=True, blank=True) def __str__(self): return self.author.username + ', ' + self.title[:40] def get_absolute_url(self): return reverse('blogpost-detail', kwargs={'pk': self.pk}) # MainApp/views.py class BlogPostDetailView(DetailView): model = BlogPost # template_name = MainApp/BlogPost_detail.html # context_object_name = 'object' Enter fullscreen mode Exit fullscreen modeFirst things first: we need to create our BlogComment model in our models.py. We can personalize our model in any way we want, I'll stick to the basics of a comment and will add the following fields: author, content/body, and a date (when the comment was posted):
# models.py from django.db import models from django.utils import timezone from django.contrib.auth.models import User class BlogComment(models.Model): blogpost_connected = models.ForeignKey( BlogPost, related_name='comments', on_delete=models.CASCADE) author = models.ForeignKey(User, on_delete=models.CASCADE) content = TextField() date_posted = models.DateTimeField(default=timezone.now) def __str__(self): return str(self.author) + ', ' + self.blogpost_connected.title[:40] Enter fullscreen mode Exit fullscreen mode- Every BlogComment will have an id (foreign key) of its BlogPost (a BlogPost can have multiple BlogComments), and if a BlogPost is deleted, then all the BlogComments that were linked to that BlogPost will be deleted (on_delete == CASCADE).
- The author will also be a foreign key to the whole User object (that has its User id, username, email, etc), therefore a User can have multiple comments.
- We will also add the magic method str() to view the comments in a more readable way (instead of viewing the object type) when we are making queries from our CLI/Admin panel).
In models.py, in our BlogPost model, we can also write a function that will return the number of comments of a blog post:
# models.py class BlogPost(models.Model): title = models.CharField(max_length=100) ... @property def number_of_comments(self): return BlogComment.objects.filter(blogpost_connected=self).count() Enter fullscreen mode Exit fullscreen modeAfter every change in the models.py file, we need to open our terminal and make the migrations to our database:
# CLI/Terminal >> cd C:\Projects\...\YourDjangoAppMainFolder >> python manage.py makemigrations >> python manage.py migrate Enter fullscreen mode Exit fullscreen modeLet's create a new form class: In the same folder as our models.py, create a new file named forms.py, where we'll write the following:
# forms.py from django import forms from .models import BlogComment class NewCommentForm(forms.ModelForm): class Meta: model = BlogComment fields = ['content'] Enter fullscreen mode Exit fullscreen modeTrès bien, now we can add to our get_context_data function within our class-based view BlogPost DetailView, in views.py, the following:
# views.py from .models import BlogPost, BlogComment from .forms import NewCommentForm class BlogPostDetailView(DetailView): model = BlogPost # template_name = MainApp/BlogPost_detail.html # context_object_name = 'object' def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) comments_connected = BlogComment.objects.filter( blogpost_connected=self.get_object()).order_by('-date_posted') data['comments'] = comments_connected if self.request.user.is_authenticated: data['comment_form'] = NewCommentForm(instance=self.request.user) return data Enter fullscreen mode Exit fullscreen modeHere we will retrieve all the comments from our current BlogPost object, store them (the query) in a local variable comments_connected, then send it further as a context to our HTML-based blogpost_detail.
However, in order to post comments directly from our class-based BlogPost DetailView, we also need to define a post method to receive the context from our form (situated in this view/html). Therefore, in the same class, we need to add:
# views.py class BlogPostDetailView(DetailView): ... def get_context_data(self, **kwargs): ... def post(self, request, *args, **kwargs): new_comment = BlogComment(content=request.POST.get('content'), author=self.request.user, blogpost_connected=self.get_object()) new_comment.save() return self.get(self, request, *args, **kwargs) Enter fullscreen mode Exit fullscreen modeFinally, in our blogpost_detail.html let's write in the DjangoTemplateLanguage the following:
<!-- COMMENTS --> <h2>Leave your comment!</h2> <div id="comments_section"> {% if user.is_authenticated %} <form method="POST"> {% csrf_token %} <div class="form-group"> {{ comment_form }} <button class="btn btn-info" type="submit">Add comment <i class="fas fa-comments"></i></button> </div> </form> {% else %} <a class="btn btn-outline-info" href="{% url 'login' %}?next={{request.path}}">Log in to add a comment!</a><br> {% endif %} {% if comments %} <strong class="text-secondary">{{ object.number_of_comments }} Comment{{ object.number_of_comments|pluralize }}</strong> <hr> <ul> {% for comment in comments %} <li> <div> <span> <strong class="text-info">{{ comment.author }} </strong> <small class="text-muted">{{ comment.date_posted }}</small> </span> <p> {{ comment.content|safe }} </p> </div> </li> {% endfor %} </ul> {% else %} <strong class="text-secondary">No comments yet...</strong> {% endif %} </div> Enter fullscreen mode Exit fullscreen modeSooo, there's a lot of code there, let's go through some parts of it step by step:
- The first thing that we do is to check if the user is authenticated: if True, then show the user a form where he can write the content of his new comment. if False, then show the user a button that redirects him to the Login page. Also, it's important that after a user logs into his account to redirect him to the earlier blog post that he wanted to post a comment, so we'll add to our redirect link "?next={{request.path}}" where request.path is the current page path (e.g. localhost/blogpost/7)
- Then we check if our current blogpost has any comments, if not, we'll put in our HTML "No comments yet..", but if we have any comments, we will then write the number of comments and then we will loop through each of them and show its author, date, and content.
Perfect, so we are almost done! However, this comment section will look rather dull... Therefore we need to create some styling, that we can find on Google or... here (thanks to bootdey.com)! To integrate it, we just need to add the CSS code in our blogpost_detail.html and the corresponding tags in our <div> and <ul> (list) sections, following their example.
Nice, now we are done! Hope you will find this useful. 😁 You can see a live example of this comment section on my blog: codingtranquillity.herokuapp.com... where you can also find more articles like this!
Have a nice day and... Happy coding! R.B.
Templates let you quickly answer FAQs or store snippets for re-use.
Submit Preview Dismiss Collapse Expand
Madeleine Atkins Follow - Joined Mar 9, 2022
- Copy link
- Hide
Thanks for sharing this wonderful post. Homepage
Collapse Expand
Radu-Alexandru B Follow Passionate about Software/Full-Stack/Application Development and Machine Learning. - Location Bucharest, Romania
- Education Computer Science & Master's Degree in Computer Vision
- Work Software Developer
- Joined Sep 21, 2020
- Copy link
- Hide
Glad that I helped you!
Welcome de DEV Community, have a wonderful week!
Collapse Expand
Ray Besiga Follow - Joined Jan 11, 2018
- Copy link
- Hide
Hi @Radu,
Great article but I just want to point out a few issues. The first is in your BlogPostDetailView
def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) comments_connected = BlogComment.objects.filter( blogpost_connected=self.get_object()).order_by('-date_posted') Enter fullscreen mode Exit fullscreen modeThis is bound to throw a Type Error. In this case, it will be an object not callable error.
The other issue I see arising is with the post:
def post(self, request, *args, **kwargs): new_comment = BlogComment(content=request.POST.get('content'), author=self.request.user, blogpost_connected=self.get_object()) Enter fullscreen mode Exit fullscreen modeThe blogpost_connected should be an instance of the BlogPost and not the BlogComment. I hope this is helpful. Best regards.
Collapse Expand
Nina Noleto Follow - Location Berlin, Germany
- Joined Nov 17, 2020
- Copy link
- Hide
Hey, thanks for this mini tutorial! I appreciate that it's detailed but simple. Made an account here just to say thanks, it kinda saved me today :)
Collapse Expand
Radu-Alexandru B Follow Passionate about Software/Full-Stack/Application Development and Machine Learning. - Location Bucharest, Romania
- Education Computer Science & Master's Degree in Computer Vision
- Work Software Developer
- Joined Sep 21, 2020
- Copy link
- Hide
Thank you!
I like to break the whole code into smaller steps, without puzzling the viewer with extra complexity or missing code parts. However, it's hard to find the extra time to write those kinds of posts, but appreciations like these motivate me.
Welcome to DEV Community!
Collapse Expand
IanRMaher Follow - Joined Mar 3, 2021
- Copy link
- Hide
Thanks for sharing this post with us I was searching for these tip from many days then my best friend suggests me your post now she need help regarding dissertation proofreading services and I want to help her. So if anyone knows about this please let me know.
Collapse Expand
leandropaolo1 Follow - Joined Jul 21, 2021
- Copy link
- Hide
great tutorial, awesome code. Thanks man, you made my day. I also made an account today just to leave you a message. PS it never happens
Collapse Expand
simfpayn Follow - Joined Apr 11, 2022
- Copy link
- Hide
Students should check for certain terms while examining the best cv writing service london as some essay reviews regularly utilize keywords such as me and I, signaling that they are aiming to generate credibility and obtain confidence. Because they want to be honest, genuine customers or users prefer nouns.
Collapse Expand
Dostow**-> Follow Web developer, Sys Admin and Technical Support L2 ᕙ(`▿´)ᕗ - Location Ávila. Spain.
- Work Technical Support Engineer - Backend Developer - Tehcnical Support L2
- Joined Oct 15, 2020
- Copy link
- Hide
What if i don't need the user to be logged in? I'm working in a comment system for my personal site without a registration system :-) Thanks.
Collapse Expand
Radu-Alexandru B Follow Passionate about Software/Full-Stack/Application Development and Machine Learning. - Location Bucharest, Romania
- Education Computer Science & Master's Degree in Computer Vision
- Work Software Developer
- Joined Sep 21, 2020
- Copy link
- Hide
Hmm... I didn't think about this option. But the process should be even simpler than the instructions I've written here.
Firstly, in our models.py we don't need an author based on User anymore, we will just use TextField():
# models.py from django.db import models from django.utils import timezone class BlogComment(models.Model): blogpost_connected = models.ForeignKey( BlogPost, related_name='comments', on_delete=models.CASCADE) author = TextField() content = TextField() date_posted = models.DateTimeField(default=timezone.now) def __str__(self): return self.blogpost_connected.title[:40] Enter fullscreen mode Exit fullscreen mode[Optional] We can still return the number of comments if we want in our main BlogPost class model in models.py:
# models.py class BlogPost(models.Model): title = models.CharField(max_length=100) ... @property def number_of_comments(self): return BlogComment.objects.filter(blogpost_connected=self).count() Enter fullscreen mode Exit fullscreen modeMake the changes to our database:
# CLI/Terminal >> cd C:\Projects\...\YourDjangoAppMainFolder >> python manage.py makemigrations >> python manage.py migrate Enter fullscreen mode Exit fullscreen modeCreate the same forms.py with an author:
# forms.py from django import forms from .models import BlogComment class NewCommentForm(forms.ModelForm): class Meta: model = BlogComment fields = ['author', 'content'] Enter fullscreen mode Exit fullscreen modeAnd now in views.py we don't need to check if self.request.user.is_authenticated anymore, we will just send the form as context to our HTML-based blogpost_detail.html.
# views.py from .models import BlogPost, BlogComment from .forms import NewCommentForm class BlogPostDetailView(DetailView): model = BlogPost # template_name = MainApp/BlogPost_detail.html # context_object_name = 'object' def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) comments_connected = BlogComment.objects.filter( blogpost_connected=self.get_object()).order_by('date_posted') data['comments'] = comments_connected data['comment_form'] = NewCommentForm( ) return data def post(self, request, *args, **kwargs): new_comment = BlogComment(content=request.POST.get('content'), author=request.POST.get('author'), blogpost_connected=self.get_object()) new_comment.save() return self.get(self, request, *args, **kwargs) Enter fullscreen mode Exit fullscreen modeAnd finally in our blogpost_detail.html we'll show directly the forms without checking if user.is_authenticated.
<!-- COMMENTS --> <h2>Leave your comment!</h2> <div id="comments_section"> <form method="POST"> {% csrf_token %} <div class="form-group"> {{ comment_form }} <button class="btn btn-info" type="submit">Add comment <i class="fas fa-comments"></i></button> </div> </form> {% if comments %} <strong class="text-secondary">{{ object.number_of_comments }} Comment{{ object.number_of_comments|pluralize }}</strong> <hr> <ul> {% for comment in comments %} <li> <div> <span> <strong class="text-info">{{ comment.author }} </strong> <small class="text-muted">{{ comment.date_posted }}</small> </span> <p> {{ comment.content|safe }} </p> </div> </li> {% endfor %} </ul> {% else %} <strong class="text-secondary">No comments yet...</strong> {% endif %} </div> Enter fullscreen mode Exit fullscreen modeHope all of these will work. Good Luck!
Collapse Expand
tejkrishna Follow - Joined Dec 29, 2020
- Copy link
- Hide
Thanks for publishing Python information. This is very understanding and useful to me
Collapse Expand
Dostow**-> Follow Web developer, Sys Admin and Technical Support L2 ᕙ(`▿´)ᕗ - Location Ávila. Spain.
- Work Technical Support Engineer - Backend Developer - Tehcnical Support L2
- Joined Oct 15, 2020
- Copy link
- Hide
Amazing!! Thank you a lot!! It's more or less how i thought it should be, now i can continue with the project :-D
Collapse Expand
salim1421 Follow - Joined Aug 6, 2024
- Copy link
- Hide
Thanks Man
Collapse Expand
Jastine Trodo Follow - Joined Feb 8, 2022
- Copy link
- Hide
masterbundles.com/stock-content/ve...
Collapse Expand
Birendra Bohara Follow - Joined Nov 25, 2020
- Copy link
- Hide
what should i do if i have to use same comment model in different place
View full discussion (17 comments) Code of Conduct • Report abuseAre you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink.
Hide child comments as well
Confirm
For further actions, you may consider blocking this person and/or reporting abuse
Radu-Alexandru B Follow Passionate about Software/Full-Stack/Application Development and Machine Learning. - Location Bucharest, Romania
- Education Computer Science & Master's Degree in Computer Vision
- Work Software Developer
- Joined Sep 21, 2020
We're a place where coders share, stay up-to-date and grow their careers.
Log in Create accountTag » Add Comment Section In Django
-
Creating Comments System With Django
-
Django Add Comment Section On Posts Feed - Stack Overflow
-
Build A Blog Comment Section - Django Blog #33 - YouTube
-
Django Project To Create A Comments System - GeeksforGeeks
-
Django Blog Tutorial Part 4: Posts And Comments - Dontrepeatyourself
-
Homework: Create Comment Model - Django Girls Tutorial: Extensions
-
Django Blog App - Add Bloging And Commenting To Your Django App
-
Implement A Comment System For A Blog Application With Django
-
Django - Comments - Tutorialspoint
-
Adding Django Threaded Comments In Blog - Django Blog #6
-
Using Forms And Creating A Comment System In A Django Blog ...
-
Tutorial — Django-comments-xtd 2.9.7 Documentation
-
Django-comments-dab - PyPI
-
Django Comment Tag - W3Schools