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 siteactive = models.BooleanField(default=True)parent = models.ForeignKey('self', null=True, blank=True, related_name='replies')class Meta:# sort comments in chronological order by defaultordering = ('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 objectpost = get_object_or_404(Post, slug=post)# list of active parent commentscomments = post.comments.filter(active=True, parent__isnull=True)if request.method == 'POST':# comment has been addedcomment_form = CommentForm(data=request.POST)if comment_form.is_valid():parent_obj = None# get parent comment id from hidden inputtry:# id integer e.g. 15parent_id = int(request.POST.get('parent_id'))except:parent_id = None# if parent_id has been submitted get parent_obj idif parent_id:parent_obj = Comment.objects.get(id=parent_id)# if parent object existif parent_obj:# create replay comment objectreplay_comment = comment_form.save(commit=False)# assign parent_obj to replay commentreplay_comment.parent = parent_obj# normal comment# create comment object but do not save to databasenew_comment = comment_form.save(commit=False)# assign ship to the commentnew_comment.post = post# savenew_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 = Commentfields = ('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.