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'
First 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]
- 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()
After 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
Let'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']
Trรจ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
Here 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)
Finally, 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>
Sooo, 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.
Top comments (16)
Thanks for sharing this wonderful post. Homepage
Glad that I helped you!
Welcome de DEV Community, have a wonderful week!
Hi @Radu,
Great article but I just want to point out a few issues. The first is in your
BlogPostDetailView
This is bound to throw a
Type Error
. In this case, it will be anobject not callable
error.The other issue I see arising is with the post:
The
blogpost_connected
should be an instance of theBlogPost
and not theBlogComment
. I hope this is helpful. Best regards.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 :)
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!
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.
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
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.
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 useTextField()
:[Optional] We can still return the number of comments if we want in our main
BlogPost
class model inmodels.py
:Make the changes to our database:
Create the same
forms.py
with an author:And now in
views.py
we don't need to check ifself.request.user.is_authenticated
anymore, we will just send the form as context to our HTML-basedblogpost_detail.html
.And finally in our
blogpost_detail.html
we'll show directly the forms without checking ifuser.is_authenticated
.Hope all of these will work. Good Luck!
Thanks for publishing Python information. This is very understanding and useful to me
Amazing!! Thank you a lot!! It's more or less how i thought it should be, now i can continue with the project :-D
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.
Thanks Man
masterbundles.com/stock-content/ve...
what should i do if i have to use same comment model in different place