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.
Related
I have a form so an user can ask for a loan and it will tell them if it´s approved or not. The problem is not the logic, it´s the submit input that doesn't work. It will not save the form in the database or show me the errors because of the submit input. Maybe is something wrong with the succes_url? I don't know, but here's my code:
views.py:
#don't worry about the logic part of the form, it's just to show how it´s supposed to work
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('Prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
def form_valid(self, form):
user = self.request.user
cliente = Cliente.objects.get(user_id = user.id)
if not cliente.approve_loan(form.cleaned_data.get('loan_total')):
form.add_error(field=None, error='loan not approved')
return self.form_invalid(form)
else:
form.instance.customer_id = cliente
super(LoanRequest, self).form_valid(form)
return render(self.request, 'Prestamos/template/Prestamos/prestamos.html', context={'form': form, 'success_msg': 'loan approved!'})
urls.py:
urlpatterns = [
path('prestamos/', views.LoanRequest.as_view(), name = 'prestamos'),
]
forms.py:
class LoanForm(forms.ModelForm):
class Meta:
model = Prestamo #loan in English
fields = ['loan_type', 'loan_total', 'loan_date']
and the template:
<div class="container">
{%if success_msg%}
<p class="alert alert-success">{{success_msg}}</p>
{%endif%}
<form action="" method="POST">
{%csrf_token%}
{%for field in form%}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{%for error in field.errors%}
<p>{{error}}</p>
{%endfor%}
{%endfor%}
<input type="submit" value="request"></input>
</form>
</div>
models.py:
class Prestamo(models.Model):
loan_id = models.AutoField(primary_key=True)
loan_type = models.CharField(max_length=20,
choices = [('PERSONAL', 'PERSONAL'), ('HIPOTECARIO', 'HIPOTECARIO'), ('PRENDARIO', 'PRENDARIO')])
loan_date = models.DateField()
loan_total = models.IntegerField()
customer_id = models.IntegerField()
class Meta:
db_table = 'prestamo'
Well, <input> is an empty tag, it does not contain anything, so don't close it.
Additionally, I'd recommend you to make gaps between template tags, like it should be {% endfor %} not {%endfor%}.
Also, remove the empty action attribute from form, as Django always take current page route if not mentioned or empty string.
Also use novalidate on form for rendering custom errors.
Try this template:
<div class="container">
{% if success_msg %}
<p class="alert alert-success">{{success_msg}}</p>
{% endif %}
<form method="POST" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% for error in field.errors %}
<p>{{error}}</p>
{% endfor %}
{% endfor %}
<input type="submit" value="request">
</form>
</div>
Edit:
One mistake I could see the name for the view is prestamos and you have mentioned it as Prestamos, which is wrong.
So:
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
I am building a Blog App and I am working on a feature in which A user can report comment so I created another model for storing reports so i am saving which comment is reported But I placed report form in detail view so report form will be below the comment in post detail page, In which I am not getting the comment id when reporting.
models.py
class Blog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=1000)
class Comment(models.Model):
commented_by = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.CharField(max_length=1000)
class ReportComment(models.Model):
reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
reported_comment = models.ForeignKey(Comment, on_delete=models.CASCADE)
text = models.CharField(max_length=1000)
views.py
def blog_detail_page(request, blog_id):
post = get_object_or_404(Blog, pk=blog_id)
if request.method == 'POST':
reportform = CommentReportForm(data=request.POST)
if FlagCommentForm.is_valid():
form = reportform.save(commit=False)
# Saving in this line
flagForm.reported_comment = reportform.id
form.reported_by = request.user
form.save()
return redirect('home')
else:
reportform = CommentReportForm()
context = {'reportform':reportform, 'post':post}
return render(request, 'blog_detail_page.html', context)
blog_detail_page.html
{{post.title}}
{% for comment in post.comment_set.all %}
{{comment.body}}
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ reportform }}
</table>
<button type="submit">Save</button>
</form>
</div>
{% endfor %}
What have I tried :-
I have also tried by using loop like :-
comments = post.comment_set.all()
for com in comments:
if request.method == 'POST':
......
if reportform.is_valid():
....
......
......
form.reported_by = com
But it always saved the first comment id.
Then I tried by request.POST method, Like :-
comment_ID = request.POST['comment_id']
But is showed MultiValueDictKeyError error.
I have tried many times But the id of comment is not saving with report instance.
You will need to add the primary key of the comment to the form, or to the URL where you submit the form to. For example as a hidden form element:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="comment_id" value="{{ comment.pk }}">
<table>
{{ reportform }}
</table>
<button type="submit">Save</button>
</form>
An alternative is to make a URL where you report a comment to with:
urlpatterns = [
path('comment/<int:comment_id>/report', some_view, name='report-comment')
]
then you can submit the form to that view with:
<form method="post" action="{% url 'report-comment' comment_id=comment.pk %}" enctype="multipart/form-data">
{% csrf_token %}
<table>
{{ reportform }}
</table>
<button type="submit">Save</button>
</form>
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 am working on my first Django project.
But I get following errors:
edit_file template
<form method="POST" action="{% url 'edit_file' file.id %}">
{% csrf_token %}
{{ form.errors }}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
<div class="form-group row">
<label for="id_name" class="col-sm-3 col-form-label"> File Name </label>
<div class="col-sm-4">
{% render_field form.name|add_class:"form-control" %}
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">File Path</label>
<div class="col-sm-4">
{% render_field form.directory_path|add_class:"form-control" %}
</div>
</div>
<div class="form-group">
{% render_field form.script_code|add_class:"form-control" %}
<pre id="id_script_code" style="height: 40pc;">{{ form.script_code }}</pre>
</div>
<button type="submit" class="btn btn-success mr-2">Save Changes</button>
Back
</form>
Views.py
def edit_file(request, id):
instance = get_object_or_404(File, id=id)
if request.method == "POST":
form = EditFileForm(request.POST, instance=instance)
if form.is_valid():
print('Form validation => True')
form.save()
return HttpResponse('<h1> database updated! </h1>')
else:
print('Form validation => False')
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
else:
instance = get_object_or_404(File, id=id)
form = EditFileForm(request.POST or None, instance=instance)
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
forms.py
class EditFileForm(ModelForm):
# field_order = ['field_1', 'field_2']
class Meta:
print("forms.py 1")
model = File
fields = ('name', 'script_code', 'directory_path','version')
def clean(self):
print("forms.py 2")
cleaned_data = super(EditFileForm, self).clean()
name = cleaned_data.get('name')
print("cleaned data: ", cleaned_data)
Models:
Version id point to a version which contains multiple files.
class File(models.Model):
# Incrementing ID (created automatically)
name = models.CharField(max_length=40)
script_code = models.TextField() # max juiste manier?
directory_path = models.CharField(max_length=200)
version = models.ForeignKey('Version', on_delete=models.CASCADE)
class Meta(object):
db_table = 'file' # table name
class Version(models.Model):
# Incrementing ID (created automatically)
version_number = models.CharField(max_length=20)
pending_update = models.BooleanField(default=False)
creation_date = models.DateTimeField(auto_now_add=True, null=True, editable=False)
modification_date = models.DateTimeField(auto_now_add=True, null=True)
connecthor = models.ForeignKey('ConnecThor', on_delete=models.CASCADE)
def __str__(self):
return self.connecthor_id
The problem:
form.is_valid() keeps failing. My view returns one error.
*version: This field is required. But I don't know how to fix this. Users should only be able to update 3 of the 5 data fields. So there is no reason to show the PK or FK in the template.
You've included version in the list of fields in your form, but you aren't outputting it in the template so there is no means of providing it. Since the model field does not specify blank=True, it is a required field, hence the error.
If you don't want users to be able to modify this field, you should remove it from that list of fields under Meta.
You do not have version in your template. Your model field for version does not say it can have null values. Hence your form validation fails. Include it in your template or remove the field from EditFileForm class's Meta class in forms.py
Not sure what I did wrong. I'm making a commenting system that users can post comment on a post and be able to reply to that comment. Every thing works fine except when user reply to some comment that reply is being shown as a single comment.(and the it's shown as a reply as well) so if I reply to comment "how are you" with a message "good", then I have created two "good". one as the reply. one as the single comment. I've looked around my code, but I have no idea why this is happening. Any help would be highly appreciated. Thank you/.Here's my code.
views.py
def post(request, slug):
user = get_object_or_404(User,username__iexact=request.user)
try:
profile = MyProfile.objects.get(user_id=request.user.id)
# if it's a OneToOne field, you can do:
# profile = request.user.myprofile
except MyProfile.DoesNotExist:
profile = None
post = get_object_or_404(Post, slug=slug)
post.views += 1 # increment the number of views
post.save() # and save it
path = request.get_full_path()
comments = Comment.objects.filter(path=path)
#comments = post.comment_set.all()
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
parent_id = request.POST.get('parent_id')
parent_comment = None
if parent_id is not None:
try:
parent_comment = Comment.objects.get(id=parent_id)
except:
parent_comment = None
comment_text = comment_form.cleaned_data['comment']
new_comment = Comment.objects.create_comment(
user=MyProfile.objects.get(user=request.user),
path=request.get_full_path(),
text=comment_text,
post = post,
parent = parent_comment
)
comment_form = CommentForm()
return HttpResponseRedirect(post.get_absolute_url())
for c in comments:
c.get_children()
context_dict = {
'post' :post,
'profile' :profile,
'comments' : comments,
'comment_form':comment_form
}
return render(request, 'main/post.html', context_dict)
models.py
class Comment(models.Model):
def get_children(self):
if self.is_child:
return None
else:
return Comment.objects.filter(parent=self)
post.html
<h1>Comments/Questions</h1>
<form method="POST" action=''>{% csrf_token %}
{{ comment_form.as_p }}
<input type='submit' class='btn btn-default' value='Add comment'/>
</form>
<br/>
<hr/>
<table class='table'>
{% for comment in comments %}
<tr><td>{{ comment.get_comment }}
<br/><small>via {{ comment.user }} | {{ comment.timestamp|timesince }} ago </small>
{% if not comment.is_child %}
<ul>
{% for child in comment.get_children %}
<li>{{ child.get_comment }}
<small>via {{ child.user }}</small>
</li>
{% endfor %}
</ul>
<a href='#' class='reply_btn'>Reply</a>
<div class='reply_comment'>
<form method="POST" action=''>{% csrf_token %}
<input type='hidden' name='parent_id' value='{{ comment.id }}' />
{{ comment_form.as_p }}
<input type='submit' class='btn btn-default' value='Add reply'/>
</form>
</div>
{% endif %}
</td></tr>
{% endfor %}
</table>
</div>
And the comment is displayed in post.html and for that page, view is
def post(request, slug):
user = get_object_or_404(User,username__iexact=request.user)
try:
profile = MyProfile.objects.get(user_id=request.user.id)
# if it's a OneToOne field, you can do:
# profile = request.user.myprofile
except MyProfile.DoesNotExist:
profile = None
post = get_object_or_404(Post, slug=slug)
post.views += 1 # increment the number of views
post.save() # and save it
path = request.get_full_path()
comments = Comment.objects.filter(path=path)
#comments = post.comment_set.all()
comment_form = CommentForm(request.POST or None)
for c in comments:
c.get_children()
context_dict = {
'post' :post,
'profile' :profile,
'comments' : comments,
'comment_form':comment_form
}
return render(request, 'main/post.html', context_dict)
The duplicate comments are just a result of your template.
It is happening because when you loop through all comments, the newly made comment is included in the comments too.
Besides acting as a comment child, it is also a comment object and that is why it is included in commments when you filter() comments with a supplied path and then it is rendered in template as a normal comment object.
For fixing this, you need to use recursive tree. There are available django apps like django-mptt which comes handy in this type of issues.
After integration it into your comments model, you can simply use the {% recursetree %} templatetag to render comments without duplication.