I was wondering if someone could help me out here, I'm struggling to group my IF statements together in such a way to achieve my results. I'm pretty new to python/django so any advice would be greatly appreciated.
I'm trying to make a 'Like' and a 'Dislike' button for my webpage. I have it all set up and I can have each individual element working but I can't seem to group them. What I'm trying to say is this, this will allow me to 'like' my post:
class PostLike(View):
def post(self, request, slug, *args, **kwargs):
post = get_object_or_404(Post, slug=slug)
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
else:
post.likes.add(request.user)
return HttpResponseRedirect(reverse('image_details', args=[slug]))
This will allow me to 'dislike' my post:
class PostDislike(View):
def post(self, request, slug, *args, **kwargs):
post = get_object_or_404(Post, slug=slug)
if post.dislikes.filter(id=request.user.id).exists():
post.dislikes.remove(request.user)
else:
post.dislikes.add(request.user)
return HttpResponseRedirect(reverse('image_details', args=[slug]))
As you can see, my code is basically duplicated and they both work well individually, what I'm trying to do is combine them into one function where if I have already 'liked' my page, I can click the 'dislike' button to dislike my page and remove my 'like'
Many thanks in advance!
models.py:
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
featured_image = CloudinaryField('image', default='placeholder')
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="blog_posts")
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, related_name='image_likes', blank=True)
dislikes = models.ManyToManyField(User, related_name='image_dislikes', blank=True)
class Meta:
ordering = ["-created_on"]
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
def number_of_dislikes(self):
return self.dislikes.count()
image_details.html (where it is rendered):
<form action="{% url 'post_like' post.slug %}" method="POST">
{% csrf_token %}
{% if liked %}
<span class="text-secondary like-buttons"><strong>{{ post.number_of_likes }} = </strong>
<button type="submit" name="post_id" class="btn like-buttons" value="{{post.slug}}">
<ion-icon name="heart-outline"></ion-icon>
</button>
</span>
<span class="text-secondary like-buttons"><strong>{{ post.number_of_dislikes }} = </strong>
<button type="submit" name="post_id" class="btn like-buttons" value="{{post.slug}}">
<ion-icon name="heart-dislike-outline"></ion-icon>
</button>
</span>
{% else %}
<span class="text-secondary like-buttons"><strong>{{ post.number_of_likes }} = </strong>
<button type="submit" name="post_id" class="btn like-buttons" value="{{post.slug}}">
<ion-icon name="heart-outline"></ion-icon>
</button>
</span>
<span class="text-secondary like-buttons"><strong>{{ post.number_of_dislikes }} = </strong>
<button type="submit" name="post_id" class="btn like-buttons" value="{{post.slug}}">
<ion-icon name="heart-dislike-outline"></ion-icon>
</button>
</span>
</form>
Again, thanks for the answers so far!
Related
Following this tutorial, I am creating a simple ecommerce website. I have data stored in a Django model, and I am calling it in my view, but it will not display in my html. Basically, I want it to create one of the boxes bellow for every donation in the donation model, but it will not work. Can someone please help me? I know this seems like an easy fix, I just can't wrap my head around it. My code is down bellow.
View:
def availablesupplies(request):
donations = Donation.objects.all()
context = {'donations':donations}
return render(request, 'availablesupplies.html')
Model:
class Donation(models.Model):
title = models.CharField(max_length=30)
phonenumber = models.CharField(max_length=12)
category = models.CharField(max_length=20)
quantity = models.IntegerField(blank=True, null=True,)
location = models.CharField(max_length=50, blank=True, null=True,)
description = models.TextField()
datedonated = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
blank=True,
null=True,
)
HTML:
<div class="row">
{% for donation in donations %}
<div class="col-lg-4">
<img class="thumbnail" src="{% static 'images/gooddeedplaceholderimage.png' %}">
<div class="box-element product">
<h6><strong>Product</strong></h6>
<hr>
<button class="btn btn-outline-secondary add-btn">Add to Cart</button>
<a class="btn btn-outline-success" href="#">View</a>
<h4 style="display: inline-block; float: right"><strong>$20</strong></h4>
</div>
</div>
{% endfor %}
</div>
{% endblock content %}
You need to pass the context object to the render engine:
def availablesupplies(request):
donations = Donation.objects.all()
context = {'donations':donations}
# pass the context ↓
return render(request, 'availablesupplies.html', context)
I am trying to implement some favourite function in my application. but when using the get_objects_or_404, I am getting this weird error.
I am trying to make a user click an icon to add a product to the favourite list, in the tutorial I was following, I noticed that it was only possible when he has the post detail, so he only added access to click the icon to add the post to favourite on the post detail page, so he was able to do something like "post = get_object_or_404(Post, id=id)" while passing the id into the request. but the case is different in my case whereby I have all of the products on a page, and I am not utilizing a product detail page, so I wan a user to as well be able to click the icon on the products page
models.py
class Product(models.Model):
name = models.CharField(max_length=36)
price = models.PositiveIntegerField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_favourite = models.ManyToManyField(User, related_name='favourite', blank=True)
views.py
def my_favourite_list(request):
user = request.user
favourite_posts = user.is_favourite.all()
context = {"favourite_posts":favourite_posts}
return render(request, "core/my-fav-products.html", context)
#login_required
def Products(request):
title = "All Products"
products = Product.objects.annotate(
favourited_by_user=Count(Case(
When(
is_favourite=request.user,
then=1
),
default=0,
output_field=BooleanField(),
)),
).order_by('-id')
my_products = Product.objects.filter(user=request.user).order_by('-id')
context = {"products":products, "my_products":my_products, "title":title}
return render(request, 'core/products.html', context)
def change_favourite(request, pk):
product = get_object_or_404(Product, pk=pk)
if request.method == "POST":
if Product.objects.filter(pk=pk, is_favourite=request.user).exists():
product.is_favourite.remove(request.user)
else:
product.is_favourite.add(request.user)
return redirect('product')
products.html
{% for product in products %}
<div class="col-md-4">
<div class="card">
<img class="card-img-top" style="height: 200px!important;" src="{{product.image.url}}" alt="Card image cap">
<div class="card-body">
<div class="card-title mb-3">
<span>{{product.name}}</span>
<span style="float: right;">
<form action="{% url 'add-favourite' product.id %}" method="post" style="display: inline-flex;">
{% csrf_token %}
<button type="submit">
{% if product.favourited_by_user %}
<i style="font-size: 22px!important;" class="fas fa-star m-r-10"></i>
{% else %}
<i style="font-size: 22px!important;" class="far fa-star m-r-10"></i>
{% endif %}
</button>
</form>
<i style="font-size: 22px!important;" class="zmdi zmdi-edit m-r-10"></i>
<i style="font-size: 22px!important;" class="zmdi zmdi-delete"></i>
</span>
</div>
<p class="card-text">
<span>Price: {{product.price}}</span>
<span style="float: right;">Quantity: {{product.quantity}}</span>
</p>
</div>
</div>
</div>
{% endfor %}
Firstly fix your view Products. (also ideally function names should be in snake_case, meaning it should be named products).
from django.db.models import Case, Count, BooleanField, When
def Products(request):
title = "All Products"
products = Product.objects.annotate(
favourited_by_user=Count(Case(
When(
is_favourite=request.user,
then=1
),
output_field=BooleanField(),
)),
).order_by('-id')
my_products = Product.objects.filter(user=request.user).order_by('-id')
context = {"products":products, "my_products":my_products, "title":title}
return render(request, 'core/products.html', context)
Now write another view for the favouriting logic:
from django.shortcuts import redirect
def change_favourite(request, pk):
product = get_object_or_404(Product, pk=pk)
if request.method == "POST":
if Product.objects.filter(pk=pk, is_favourite=request.user).exists():
product.is_favourite.remove(request.user)
else:
product.is_favourite.add(request.user)
return redirect('view-name-of-products-list')
Now add a url for this view:
urlpatterns = [
...
path('add-to-favourite/<int:pk>/', change_favourite, name='add-favourite'),
...
]
Now in your html simply put those buttons in a form:
<form action="{% url 'add-favourite' product.pk %}" method="post">
<button type="submit">{% if product.favourited_by_user %}Un-favourite Product{% else %}Favourite Product{% endif %}</button>
</form>
Style the buttons as you want.
I'm trying to modify a page that essentially keeps tracks of when users enter/leave an area. Currently all the logic works fine, but I would like to add the functionality to ask a user to enter their own "estimate" of the time they entered/left in the case they forgot to do so, since these instances get flagged upon submission. I'm fairly new to Django/Python, and I have a very basic understanding of how this all works, so I apologize in advance if my suggestions on handling this are a bit off.
These is a summarized version of what the models look like:
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False, help_text="Work Area", related_name="work_area")
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, help_text="Station", related_name="stations", blank=True)
edited_timestamp = models.DateTimeField(null=True, blank=True)
time_exceptions = models.CharField(max_length=2, blank=True)
time_in = models.DateTimeField(help_text="Time in", null=True, blank=True)
time_out = models.DateTimeField(blank=True, help_text="Time out", null=True)
def __str__(self):
return self.employee_number
I figured that the easiest way to do this is by adding it to the logic in views.py, to prevent updating a wrong record, in such a way that, right after an entry gets flagged, the user is prompted to enter the date/time they estimate they entered an area.
To keep code readable, I'm only showing the case where entries get flagged. If someone tries to enter any area, the forgets to exit, and then attempts to enter an area again, the record without an exit gets immediately flagged (the time_exceptions='N' is the flag).
views.py
class EnterExitArea(CreateView):
model = EmployeeWorkAreaLog
template_name = "operations/enter_exit_area.html"
form_class = WarehouseForm
def form_valid(self, form):
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
if 'enter_area' in self.request.POST:
form.save()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(work_area=area) & Q(time_out__isnull=True) & Q(time_in__isnull=True)) & (Q(station_number=station) | Q(station_number__isnull=True))).update(time_in=datetime.now())
# If employee has an entry without an exit and attempts to enter a new area, mark as an exception 'N'
if EmployeeWorkAreaLog.objects.filter(Q(employee_number=emp_num) & Q(time_out__isnull=True) & Q(time_exceptions="")).count() > 1:
first = EmployeeWorkAreaLog.objects.filter(employee_number=emp_num, time_out__isnull=True).order_by('-time_in').first()
EmployeeWorkAreaLog.objects.filter((Q(employee_number=emp_num) & Q(time_out__isnull=True))).exclude(pk=first.pk).update(time_exceptions='N')
# Here would be the rendering of the page, maybe as a popup? and then saving the entered field using edited = form.cleaned_data['edited_timestamp'] and then saving it using .update()
return HttpResponseRedirect(self.request.path_info)
I was trying to do this in a way that instead of redirecting to the same page, it would render something similar to the page below, which just requests for the time, and after the user submits, it saves it into the records' "edited_timestamp" field.
update_edited_timestamp.html
<div>
<form id="warehouseForm" action="" method="POST" class="form-horizontal">
<label>Enter estimated time:</label>
<input value="html()" type="datetime-local" name="edited_timestamp">
</form>
</div>
Adding my main html page where this all takes place just in case
enter_exit_area.html
{% extends "operations/base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" class="form-horizontal" novalidate >
{% csrf_token %}
<div>
<div>
<div style="color: red">{{ form.employee_number.errors.as_text }}</div>
<label>Employee</label>
{{ form.employee_number }}
</div>
<div>
<div style="color: red">{{ form.work_area.errors.as_text }}</div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div style="color: red">{{ form.station_number.errors.as_text }}</div>
<div>
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
<button type="submit" name="leave_area" value="Leave">Leave Area</button>
</div>
</div>
</form>
{% endblock main %}
I was trying to add a comment function to my django app, but whatever I did, the comment submitted just wouldn't show up in database, can't figure out why. Here're some codes I worked with, please have a look.
the models.py part
class Comment(models.Model):
name = models.CharField(max_length=80)
email = models.EmailField()
text = models.TextField()
post = models.ForeignKey(Post, related_name='comments')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
verbose_name="评论"
verbose_name_plural="评论"
def __str__(self):
return 'Comment by {} on {}'.format(self.name, self.post)
the forms.py part
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model=Comment
fields=('name','email','text')
the views.py part
def post_comment(request,id):
post=get_object_or_404(Post,id=id)
if request.method=='POST':
form=CommentForm(request.POST)
if form.is_valid():
name=request.POST.get('name','')
email=request.POST.get('email','')
text=request.POST.get('text','')
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
return redirect(post)
else:
form=CommentForm()
comment_list = post.comments.all()
context={
'post':post,
'form':form,
'comment_list':comment_list
}
return render(request,'post_detail.html',context=context)
return redirect(post)
and the post_detail.html part
<form action="" method="POST" class="mb-5">{% csrf_token %}
<div class="form-group">
<label for="exampleInputPassword1">名称</label>
<input name="name" type="name" class="form-control" id="exampleInputPassword1" placeholder="Name">
</div>
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input name="email" type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="exampleFormControlTextarea1">评论</label>
<textarea name="text" class="form-control" id="exampleFormControlTextarea1" placeholder="Comment area..." rows="5"></textarea>
</div>
<button type="submit" class="btn btn-primary" value="post_comment">提交</button>
</form>
Any suggestions? Thanks.
You are doing two things wrong here. The first is that you are rendering the form manually and you do not have anything in your HTML to display form errors. You need to add the errors to your HTML as described in the documentation - or until yo get this sorted out use {{ form.as_p }}
Even after that, it still wont work because your python code in your view is wrong.
if form.is_valid():
name=request.POST.get('name','')
email=request.POST.get('email','')
text=request.POST.get('text','')
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
return redirect(post)
else:
form=CommentForm()
The last two lines of code above results in a new form being created when teh form is invalid. That discards any form data that was posted. Which also means any errors you had will be discarded along the way. The correct code will look something like the following:
post=get_object_or_404(Post,id=id)
if request.method=='POST':
form=CommentForm(request.POST)
if form.is_valid():
# don't directly use the request post data. Always use form.cleaned_data
name= form.cleaned_data['name'
.....
comment=Comment(name=name,email=email,text=text,post=14)
comment.save()
# the following redirect is wrong. Here post is an object
# you retrieved from a database, we usually redirect to a URL
return redirect(post)
else:
form = CommentForm()
comment_list = post.comments.all()
context={
'post':post,
'form':form,
'comment_list':comment_list
}
return render(request,'post_detail.html',context=context)
try this to creating comment class:
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
Register Comment model in the admin panel
To register the Comment model in the admin panel, go to blog/admin.py and add this line:
admin.site.register(Comment)
directly under this line:
admin.site.register(Post)
Remember to import the Comment model at the top of the file, too, like this:
from django.contrib import admin
from .models import Post, Comment
admin.site.register(Post)
admin.site.register(Comment)
Make our comments visible HTML:
<hr>
{% for comment in post.comments.all %}
<div class="comment">
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
I'm making my own blog with Django and I already made a Comments system.. I want to add the replies for each comment (like a normal comment's box) and I don't know what to do this is my current models.py comments:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateField(auto_now_add=True)
parent = models.ForeignKey('self', null=True, related_name='replies')
def __str__(self):
return self.text
and this is the .html where I use the comments
{% for comment in post.comments.all %}
<ul>
{{ comment.text }}
{% for reply in comment.replies.all %}
<li>
{{ reply.text }}
</li>
{% endfor %}
<ul>
{% endfor %}
and apparently It is working but when I try to make a comment in the admin site of Django it forces me to put a "Parent" to each comment (and this is not obligatory beacuse not every comment is a reply) I also don't know how to add the reply "button" in the HTML file. Please help tell me what changes can I do to make a simple comment box with replies . Thanks a lot
I had the same problem and resolved it as follows:
1.
For admin site as mentioned above just set blank=True for parent field. My comment model:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField(max_length=200, blank=True)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
# manually deactivate inappropriate comments from admin site
active = models.BooleanField(default=True)
parent = models.ForeignKey('self', null=True, blank=True, related_name='replies')
class Meta:
# sort comments in chronological order by default
ordering = ('created',)
def __str__(self):
return 'Comment by {}'.format(self.name)
remember to run makemigrations and migrate
2.Let's start with views. I'm using the post_detail
view to display the post and its comments. We add a QuerySet to retrieve all parent active comments for this post. After this, we validate the submitted data using the form's is_valid(). If the form is valid we check if submitted data comes from hidden input in replay button form. Next if parent_id exits we create parent object(parent_obj) for replay comment and replay_comment object, then we assign parent_obj to replay_comment.
If parent_obj is equal to None we just proceed with normal comment by creating new_comment object and saving it to the database.
def post_detail(request, post):
# get post object
post = get_object_or_404(Post, slug=post)
# list of active parent comments
comments = post.comments.filter(active=True, parent__isnull=True)
if request.method == 'POST':
# comment has been added
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
parent_obj = None
# get parent comment id from hidden input
try:
# id integer e.g. 15
parent_id = int(request.POST.get('parent_id'))
except:
parent_id = None
# if parent_id has been submitted get parent_obj id
if parent_id:
parent_obj = Comment.objects.get(id=parent_id)
# if parent object exist
if parent_obj:
# create replay comment object
replay_comment = comment_form.save(commit=False)
# assign parent_obj to replay comment
replay_comment.parent = parent_obj
# normal comment
# create comment object but do not save to database
new_comment = comment_form.save(commit=False)
# assign ship to the comment
new_comment.post = post
# save
new_comment.save()
return HttpResponseRedirect(post.get_absolute_url())
else:
comment_form = CommentForm()
return render(request,
'core/detail.html',
{'post': post,
'comments': comments,
'comment_form': comment_form})
Simple comment form:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body')
* More about ModelForm
And lastly template. We need to create two forms. One form for comments and the second one for replays. Here you have simple template:
<!-- Comments Form -->
<h2>Add a new comment</h2>
<form action="." method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<button type="submit">Add comment</button>
</form>
<!-- Comment with nested comments -->
{% for comment in comments %}
<div class="comment" style="background-color: powderblue">
<p class="info">{{ comment.name }} | {{ comment.created }}</p>
{{ comment.body|linebreaks }}
{% for replay in comment.replies.all %}
<p class="info">{{ replay.name }} | {{ replay.created }}</p>
<li>{{ replay.body }}</li>
{% endfor %}
<h5>Replay</h5>
<form action="." method="post">
{{ comment_form.as_p }}
{% csrf_token %}
<!-- Hidden input for parent comment.id -->
<input type="hidden" name="parent_id" value="{{ comment.id }}">
<input class="btn btn-primary" type="submit" value="Replay">
</form>
</div>
{% empty %}
<h4>There are no comments yet.</h4>
{% endfor %}
just add some nice css and maybe jquery to have fade in reply comments and that's all.
first Question:parent must be set in admin.
parent = models.ForeignKey('self', null=True, blank=True, related_name='replies')
blank=True can let you don't set parent in admin.
second Question:add comment dynamicly.
<form id="comment-form" method="post" role="form">
{% csrf_token %}
<textarea id="comment" name="comment" class="form-control" rows="4" placeholder="input comment!"></textarea>
<button type="submit" class="btn btn-raised btn-primary pull-right">submit</button>
</form>
$('#comment-form').submit(function(){
$.ajax({
type:"POST",
url:"{% url 'article_comments' article.en_title %}",
data:{"comment":$("#comment").val()},
beforeSend:function(xhr){
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
},
success:function(data,textStatus){
$("#comment").val("");
$(".comment ul").prepend(data);
},
error:function(XMLHttpRequest, textStatus, errorThrown){
alert(XMLHttpRequest.responseText);
}
});
return false;
});
view.py:
print_comment = u"<p>comment:{}</p>".format(text)
if parent:
print_comment = u"<div class=\"comment-quote\">\
<p>\
<a>#{}</a>\
{}\
</p>\
</div>".format(
parent.user.username,
parent.text
) + print_comment
# current comment
html = u"<li>\
<div class=\"comment-tx\">\
<img src={} width=\"40\"></img>\
</div>\
<div class=\"comment-content\">\
<a><h1>{}</h1></a>\
{}\
<p>{}</p>\
</div>\
</li>".format(
img,
comment.user.username,
print_comment,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
return HttpResponse(html)
models.py
class Comment(models.Model):
author = models.CharField(max_length=100)
comment_field = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
reply = models.ForeignKey('Comment', on_delete=models.CASCADE, related_name="replies", null=True)
def __str__(self):
return self.author
views.py
def post_detail(request, slug):
post = Post.objects.get(slug=slug)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
reply_obj = None
try:
reply_id = int(request.POST.get('reply_id'))
except:
reply_id = None
if reply_id:
reply_obj = Comment.objects.get(id=reply_id)
author = form.cleaned_data['author']
comment = form.cleaned_data['comment']
if reply_obj:
Comment(author=author,comment_field=comment, reply=reply_obj, post=post).save()
else:
Comment(author=author,comment_field=comment, post=post).save()
return redirect(reverse('post_detail', args=[post.slug]))
else:
form = CommentForm()
comments = Comment.objects.filter(post=post, reply=None).order_by('-date_created')
context = {
'post':post,
'form':form,
'comments':comments
}
return render(request, 'post_detail.html', context)
template (post_detail.html)
{% for comment in comments %}
{{comment.author}}
{{comment.date_created.date}}
{{comment.comment_field}}
{% for reply in comment.replies.all %}
{{reply.author}}
{{reply.date_created.date}}
{{reply.comment_field}}
{% endfor %}
<a class="text-decoration-none" data-bs-toggle="collapse" href="#collapseExample{{comment.id}}" role="button" aria-expanded="false" aria-controls="collapseExample">
Reply </a>
<div class="collapse" id="collapseExample{{comment.id}}">
<div>
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
{{ form.author }}
</div>
<div class="form-group mt-md-2">
{{form.comment}}
</div>
<input type="hidden" name="reply_id" value="{{ comment.id }}">
<button class="btn btn-primary mt-md-1" type="submit" value="Reply">Reply</button> </form>
</div>
</div>
<hr>
{% endfor %}
You need to create a field called parent which is a ForeignKey with the Comment model itself.
parent = models.ForeignKey('self' , null=True , blank=True , on_delete=models.CASCADE , related_name='replies')
Also, you can create a property called children in the Comment model which returns all the replies of the comment.
#property
def children(self):
return BlogComment.objects.filter(parent=self).reverse()
For detail explanation of comment and reply system in django you can read my article about that.