- How do I implement a commenting system on my own website?
- What are the options and possible solutions?
- Is there any ready-to-use user solutions( Django app)? With easy integration into my own website.
Commenting system for a website, using the Django framework
11.11.2023
15.04.2025
10 minutes
145
0
0
0
0
Introduction
Hi. If you, like me, are asking the same questions, like:
If so, you are in the right URL address.
In this article, I will describe my way of solving this kind of
problem, I mean commenting and replying on the website. And this system will allow
commenting, for guests, and for registered users.
Also, I want to make a note that my goal is not to make such a commenting system,
that no one has ever seen in their lives. The goal was to make commenting right fit
for my website, that's it.
And if this works for you too, great. If not, then I am sure that you always can
make some notes and learn something new for yourself.
Types and variations
How comments can be implemented? And what ways and variants to choose when implementing your own commenting system ?This is the question I asked myself when for the first time I needed to add commenting to the site.
-
I chose 2 main types of system commenting, based on who can leave comments, the rest are just variations and tips.
- Anonymous commenting (Any rogue will be able to leave a comment on your site)
- Authenticated commenting (Commenting for registered users only)
Anonymous commenting
This is the easiest to implement, but at the same time the most dangerous. Why dangerous?
Here, perhaps, I didn’t answer quite correctly, but my dangerous point was that absolutely any person or bot should leave comments. In absolutely any quantity and absolutely any quality.
You yourself understand that anonymity gives people the freedom to do and write whatever they want. And from my own experience I know
If a person has the opportunity to give a shit and even with impunity, those who wish to do so must be polite.
Authenticated commenting
Only registered users comment.
If you are creating an economic network, forum or online store, this is the comment form for you. Important note: Registration must provide some benefits to those who register.
I repeat again, if you have a blog, news portal or information portal, I do not recommend it.
What did I choose? And how it works for me.
I have a mixed one. Both anonymous and registered users can leave comments under posts.
Let's imagine you visited my website. You have read the article you are interested in and decided to leave a comment.
Scroll to the end, write what you think, and press the “Leave a comment” button.
A new form pops up when you click a button and asks you to enter your name. You entered it.
The message form has changed and your name has appeared.
You press, “Leave a comment” again. It appears on your page.
Now let's move on to the part where I explain step by step how to implement this commenting system.
Create a Comment model
Let's start with the Comment model. She will store all our comments.
-
What fields do we need?
- type ➜ field that indicates which article it relates to
- user ➜ what user left the comment (registered one)
- anonymous_user_name ➜ The name you chose for yourself
- anonymous_user_id ➜ Required to be able to create an anonymous comment from a non-anonymous one
- content ➜ actual content of comment
- timeCreated ➜ when it was written
Prepare comment and comments templates
-
In general, we need 3 templates, at least
- one for rendering one comment (will be used when you need to add a new comment)
- one for rendering a group of comments (will be used when they need to be loaded)
- and another one where these comments will be
Here is a template for one comment
{{com.content}}
{% if user.slug == com.user.slug and request.session.is_auth %}
{% endif %}
Let me clarify a couple of unclear points in this template.
Checking if a comment was written by an anonymous user
{% if not com.anonymous_user_id|length > 0%}
Checking whether the user who posted this comment has an avatar
{% if com.user.avatar|length > 0 %}
Now this piece of the template
{% if user.slug == com.user.slug and request.session.is_auth %}
{% endif %}
This is a pretty important piece, so to speak. In short, it says that only registered user
will be able to delete their comments.
In the comment template everything is absolutely the same as
for one comment. Except that it is needed to render multiple comments.
That is, it uses a for loop
Prepare a template where these comments will appear
Now let's consider the place where these comments will be displayed.
This is the code I use in base_post.html. That is, this is the template from which
I inherit all other posts (articles, cases, news). In short,
provides basic functionality for my articles. Below in the chapter "Completed application and other resources"
In truth, I use comments for more than just posts. Each registered
The user has his own account where he can view all the comments he left. And delete
if he wants.
Let's look at the part of base_post.html that is responsible for comments.
First comes the comment form itself.
...
{% csrf_token %}
Next there is a button to leave a comment. For authenticated users, she will simply leave a comment.
But for anonymous users, another button will be displayed (the button is still the same, it just has different functionality)
It will only ask for the anonymous name. After which, it will be replaced with a regular button for sending a comment.
For anonymous
{% if not request.session.is_auth %}
For registered
{% else %}
{% endif %}
Write an ajax request to leave a comment
Templates are of course good, but now we need to connect the server part with the front end. Let's write an ajax script
Don't forget to include it in base_post.html
{% block scripts %}
{% csrf_token %}
{% block scripts_post %}
{% endblock %}
{% endblock %}
A little clarification. First we include the jQuery library.
Then we save the csrf token and what post the user is viewing now
{% csrf_token %}
Next, we connect our script to make comments work (our ajax requests will be there)
And since templates for articles, news and cases are inherited from this template, it can be
separate scripts (or styles) are required.
That's why it's here.
{% block scripts_post %}
{% endblock %}
When the Leave a comment button is clicked (whether from an authorized or anonymous user)
This function is running.
function sendComment(path){
var post = post_slug
var about = $("#about").val()
var username = $("#comments_el__user_name_commentAdd").text()
$.ajax({
type: "POST",
url: "/" + language_code + "/" + path + "/",
data: {
'post': post,
'about': about,
'username': username
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
$(result).insertAfter("#comment_add")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
},
})
}
Where we are sending the name of the current post, message, and username to the server
var post = post_slug
var about = $("#about").val()
var username = $("#comments_el__user_name_commentAdd").text()
If successful, we render comment.html and paste the resulting result above all comments.
And don’t forget to add an event to be able to delete this comment.
$(result).insertAfter("#comment_add")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
Write an ajax request to load comments
Uploading comments to the server is great, but other users should also see
comments. Here is a function to display comments for a single post.
function loadComments(){
var post = post_slug
$.ajax({
type: "POST",
url: "/" + language_code + "/load_comments/",
data: {
'post': post,
'number': number,
'offset': offset,
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
$(result).insertBefore("#scroll-sentinel")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
offset = offset + number
},
})
}
It is important to note that this function is triggered when the scroll bar has reached the end.
For this purpose, a special observer is used, who monitors this.
$(document).ready(function(){
default_avatar_path = $("#comments_el__user_avatar_commentAdd").attr('src')
const observer = new IntersectionObserver((entries, observer) => {
// Loop through the entries
for (const entry of entries) {
// Check if the entry is intersecting the viewport
if (entry.isIntersecting) {
// Load more content
loadComments()
}
}
});
const scrollSentinel = document.querySelector("#scroll-sentinel");
observer.observe(scrollSentinel);
...
Write an ajax request to delete a comment
And a small bonus. How to delete comments.
From the previous sections, you saw how and where events are assigned to delete a comment.
Now let's see how it is removed.
function removeComment(toRemove){
var post = post_slug
var comment_id = $(toRemove).attr("commentid")
$.ajax({
type: "POST",
url: "/" + language_code + "/remove_comment/",
data: {
'post': post,
'comment_id': comment_id,
},
headers: {'X-CSRFToken': csrftoken},
mode: 'same-origin', // Do not send CSRF token to another domain.
success: function(result){
// Removes only client side part
$(toRemove).parent().remove()
},
})
}
As you noticed, to delete a comment you need its id. This is a custom attribute that we
we fill in when we render the comment or comments.
Here he is
{% if user.slug == com.user.slug and request.session.is_auth %}
Setting up views and paths
Almost everything is ready. All that remains is to figure out the routes and the performances themselves.
Comment/urls.py looks like this:
from django.urls import path
from .views import *
urlpatterns = [
path('send_comment_guesting/', send_comment_guesting),
path('send_comment_authorized/', send_comment_authorized),
path('remove_comment/', remove_comment),
path('prepare_user/', prepare_user),
path('load_comments/', load_comments),
path('load_comments_by_user/', load_comments_by_user),
]
Let's look at each presentation separately. Except, of course, load_comments_by_user.
You can watch this function yourself if you want. We are reviewing comments
using the example of posts.
def send_comment_guesting(request):
media_root = settings.MEDIA_URL
if request.method == 'POST':
type = Post.objects.filter(slug=request.POST['post']).get()
content = request.POST['about']
username = request.POST['username']
user_id = request.session.session_key
comment = Comment(
anonymous_user_id=user_id,
anonymous_user_name=username,
type=type,
content=content
)
comment.save()
context = {
'com': comment,
'media_root': media_root,
}
return render(request, "Comment/comment.html", context=context)
We save the comment as anonymous пользователь. Where:
- type ➜ post to which the comment belongs
- content ➜ content of comment
- username ➜ anonymous user name
- user_id ➜ current session id
Next, a record is created in the database and the page with the comment is rendered.
def send_comment_authorized(request):
media_root = settings.MEDIA_URL
if request.method == 'POST':
user = User.objects.filter(name=request.session.get('username')).get()
type = Post.objects.filter(slug=request.POST['post']).get()
content = request.POST['about']
comment = Comment(user=user, type=type, content=content)
comment.save()
context = {
'com': comment,
'user': user,
'media_root': media_root,
}
return render(request, "Comment/comment.html", context=context)
Save the comment as authorized. Almost everything is the same, only here
the user object who added the comment is added.
def prepare_user(request):
data = {
'isValid': False,
'username': None,
}
if request.method == "GET":
username = request.GET['username']
data['isValid'] = True
data['username'] = username
return JsonResponse(data)
You can consider this function a service function, because the only task of this function is
insert the username into the form to create a comment.
def remove_comment(request):
if request.method == 'POST':
comment_id = request.POST['comment_id']
comment = Comment.objects.filter(id=comment_id).get()
comment.delete()
status = 200
return JsonResponse({}, status=status)
Delete a comment by id.
def load_comments(request):
try:
user = User.objects.filter(name=request.session.get('username')).get()
except:
user = None
media_root = settings.MEDIA_URL
if request.method == 'POST':
number = request.POST.get('number', 5)
offset = request.POST.get('offset', 0)
type = Post.objects.filter(slug=request.POST['post']).get()
comments = Comment.objects.filter(type=type).order_by('-timeCreated')[(int(offset)):(int(number)) + (int(offset))]
context = {
'comments': comments,
'user': user,
'media_root': media_root,
}
return render(request, "Comment/comments.html", context=context)
Loading comments.
First, we check whether an authorized user is asking for a download or not.
try:
user = User.objects.filter(name=request.session.get('username')).get()
except:
user = None
Next, we determine how many, from which comment and from which article to load comments.
number = request.POST.get('number', 5)
offset = request.POST.get('offset', 0)
type = Post.objects.filter(slug=request.POST['post']).get()
comments = Comment.objects.filter(type=type).order_by('-timeCreated')[(int(offset)):(int(number)) + (int(offset))]
Then we render the found comments and return them to the user. It returns here:
function loadComments():
...
success: function(result){
$(result).insertBefore("#scroll-sentinel")
$(".comment_remove__button").off()
$(".comment_remove__button").one('click', function() {
removeComment(this)
})
offset = offset + number
},
Where we insert the received comments into the page, increase the counter and assign an event for deletion
comment.
Completed application and other resources
Post Scriptum. Conclusion
So, now that the article is over, I would like to apologize for the fact that the article did not turn out the way it should have.
go out. I'll explain now.
Initially, I planned to talk about 3 possible options for writing comments.
Such as:
- Anonymous
- Via social networks
- Only for registered users
But alas, as soon as I realized how much time it would take me, I decided to cool my ardor a little. And write an article
specifically about my journey of writing a commenting system. That is, a combined version of anonymous/registered
commenting.
I also wanted to insert comment forms for each type. That is, start with the simplest, then move on to
commenting through social networks, and end it all with the fact that only registered users will be able to comment.
Which, by the way, I don’t have yet.
Maybe someday I will write this article in the form I wanted, but for now we have what we have.
Comments
(0)
Send
It's empty now. Be the first (o゚v゚)ノ
Other
Similar articles
Used termins
- Pagination ⟶ Is the design pattern, which divide content on the website into smaller pieces (pages)
- Comment ⟶ A message left by a user on a website.
- Serializer ⟶ Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types.
- React component ⟶ A reusable piece of code in a React application that represents a part of the user interface (UI). Components can be either class-based or functional, and they allow developers to build complex UIs by breaking them down into smaller, manageable pieces.
Related questions
- What did you do before becoming a webmaster? I made games and wrote my own game engine. I made 5 games and released only one on the Play Store. And the engine was called **DI.** I abandoned it
- My App doesn't render correctly on the server? If it doesn't work, in 99% of cases it's a configuration issue. A missing property, a wrong call order, or a missing component – server-side rendering is strict about configuration. The best way to find out what's wrong is to compare your project to an already working setup. Check out the reference implementations, bit by bit.
- How do I extract content from dynamic web pages? A dynamic website would update the data frequently. For example, there are always new posts on Twitter. To scrape from such a website, it is the same process as scraping other websites, but it would allow the scraper to access the website with some frequency to get the continuously updated data.