I'm working on simple blog and I'm trying to implement a nested comment system for each post.
I created model for comments and it's works fine via Django Admin Page.
I don't know how to create form for posting new comment and replying.
Here is what I have so far:
models.py
(...)
class Post(models.Model):
author = models.ForeignKey('Author', on_delete=models.CASCADE)
title = models.CharField(max_length=250)
slug = models.SlugField(unique=True, blank=True, max_length=250)
created = models.DateTimeField(auto_now=False, auto_now_add=True)
modified = models.DateTimeField(auto_now=True, auto_now_add=False)
tags = TaggableManager(blank=True)
image = models.ImageField(upload_to="images/%Y/%m/", blank=True, null=True)
content = models.TextField()
def get_absolute_url(self):
return reverse('post_detail', kwargs={'slug': self.slug, })
# create slug
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(unidecode(self.title))
super(Post, self).save(*args, **kwargs)
def __str__(self):
return str(self.title)
class Comment(MPTTModel):
post = models.ForeignKey("Post", on_delete=models.CASCADE, null=True, related_name='comments')
parent = TreeForeignKey('self', null=True, blank=True, related_name='replies', db_index=True)
name = models.CharField(max_length=250)
email = models.EmailField(max_length=250, blank=True, null=True)
website = models.CharField(max_length=250, blank=True, null=True)
created = models.DateTimeField(auto_now=False, auto_now_add=True)
content = models.TextField()
def __str__(self):
return str("{}: {}...".format(self.name, self.content[:50]))
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = [ "name", 'email', 'website', 'content']
views.py
class PostDetailView(DetailView):
model = Post
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm()
return context
class PostCommentView(SingleObjectMixin, FormView):
template_name = 'blog/post_detail.html'
form_class = CommentForm
model = Post
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(PostCommentView, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('post_detail', kwargs={'slug': self.object.slug})
class PostDetail(View):
def get(self, request, *args, **kwargs):
view = PostDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = PostCommentView.as_view()
return view(request, *args, **kwargs)
comment_form.html
<div id="respond">
<h2 class="respond-title">Leave a comment</h2>
<span class="form-caution">Make sure you enter the (*) required information where indicated. HTML code is not allowed.</span>
<form action="" method="post">
{{ form.non_field_errors }}
{% csrf_token %}
{{ form }}
<input name="submit" type="submit" id="submit" class="btn btn-primary" value="Submit Comment">
</form>
</div>
Modified Preorder Tree Traversal is not necessary the best solution for building nested comments. Take into consideration that with each new node whole tree has to be rebuild. For example, lets imagine comments under post F:
// F
// / | \
// B X G
// / \
// Z Y
Notice that comment X has left value of 8 and right value of 9. But, with every new subcomment to B or X these values will change. E.g. with a new comment under comment Z values of X will change to lft = 10 and rht = 11. This could lead to awful performance and, as I imagine, loss of data in case of messenger means where information is sent only once (e.g. Django channels).
This post presents solution for achieving nested commend system by connecting inbuilt comment app and the mptt. It's an old post and the django.contrib.comments
has been separated to a separate project since Django 1.6. Recently Tim Graham added changes to support Django 2.0, so it seems up-to-date.
The idea is to create a custom model and form that will add to the title field of Django_comments. The post suggest wiring it like that:
Models.py
from django_comments.models import Comment
from mptt.models import MPTTModel, TreeForeignKey
class MPTTComment(MPTTModel, Comment):
""" Threaded comments - Add support for the parent comment store and MPTT traversal"""
# a link to comment that is being replied, if one exists
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
class MPTTMeta:
# comments on one level will be ordered by date of creation
order_insertion_by=['submit_date']
class Meta:
ordering=['tree_id','lft']
Forms.py
from django import forms
from django.forms.widgets import widgets
from django_comments.forms import CommentForm
from models import MPTTComment
class MPTTCommentForm(CommentForm):
parent = forms.ModelChoiceField(queryset=MPTTComment.objects.all(),
required=False, widget=forms.HiddenInput)
def get_comment_model(self):
# Use our custom comment model instead of the built-in one.
return MPTTComment
def get_comment_create_data(self):
# Use the data of the superclass, and add in the parent field field
data = super(MPTTCommentForm, self).get_comment_create_data()
data['parent'] = self.cleaned_data['parent']
return data
__init__.py in your app directory
from models import MPTTComment
from forms import MPTTCommentForm
def get_model():
return MPTTComment
def get_form():
return MPTTCommentForm
There is also another django package, this one with nested comments already built-in: django-threadedcomments. Worth checking out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With