Reputation: 1
My Problems are as follows:
Im relatively new to Django. so sorry for this mess. I've tried to implement threaded comments to my blog via MPTT. But when i pressing the 'Reply' Button django throws me a Type Error
Normal parent comments working correctly
When i just create a comment with via the admin site, i can create parent comments and child comments and they will be indented just as expected in the main Application too...
my Database is mySQL.
The Error:
in _calculate_inter_tree_move_values
space_target = target_right - 1
~~~~~~~~~~~~~^~~
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'
my models.py:
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from mptt.models import MPTTModel, TreeForeignKey
STATUS = (
(0,"Draft"),
(1,"Publish")
)
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete= models.CASCADE, related_name='blog_posts')
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post_detail',args=[self.slug])
class Comment(MPTTModel):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False)
parent=TreeForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, related_name='children')
class MPTTMeta:
order_insertion_by = ['created_on',]
def __str__(self):
return 'Comment {} by {}'.format(self.body, self.name)
#just for the threaded comments
def get_comments(self):
return Comment.objects.filter(parent=self).filter(active=True)
views.py:
from django.shortcuts import render, get_object_or_404, redirect, HttpResponseRedirect
from django.views import generic
#from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
#from django.template import loader
from .models import Post, Comment
from .forms import CommentForm
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
paginate_by = 3
#class PostDetail(generic.DetailView):
# model = Post
# template_name = 'post_detail.html'
class AboutMe(generic.TemplateView):
template_name = 'about_me.html'
class Contact(generic.TemplateView):
template_name = 'contact.html'
class Impressum(generic.TemplateView):
template_name = 'impressum.html'
class StaticContent(generic.TemplateView):
template_name = 'static_html_content.html'
class SideBar(generic.TemplateView):
template_name = 'sidebar.html'
#class PostDetail(generic.DetailView):
# model = Post
# context_object_name = "post"
# template_name = "post_detail.hhml"
def post_detail(request, post):
template_name = 'post_detail.html'
post = get_object_or_404(Post, slug=post, status=1)
comments = post.comments.filter(active=True)
new_comment = None
#comment posted
if request.method == 'POST':
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
# Create comment object but dont save to database yet
new_comment = comment_form.save(commit=False)
# Assign thecurrent post to the comment
new_comment.post = post
#save the comment to the database
new_comment.save()
#redirect to the same page and focus on that comment
return HttpResponseRedirect('/' + post.slug)
#return redirect(post.get_absolute_url()+'#'+str(new_comment.id))
else:
comment_form = CommentForm()
return render(request, template_name, {'post': post, 'comments': comments, 'new_comment': new_comment, 'comment_form': comment_form})
def reply_page(request):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
post_id = request.POST.get('post_id')
parent_id = request.POST.get('parent')
post_url = request.POST.get('post_url')
print(post_id)
print(parent_id)
print(post_url)
print('was here reply!')
reply = form.save(commit=False)
reply.post = Post(id=post_id)
reply.parent = Comment(id=parent_id)
reply.save()
return redirect(post_url+'#'+str(reply.id))
return redirect('/')
forms.py:
from .models import Comment
from django import forms
from mptt.forms import TreeNodeChoiceField
class CommentForm(forms.ModelForm):
parent = TreeNodeChoiceField(queryset=Comment.objects.all())
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['parent'].widget.attrs.update(
{'class': 'd-none'})
self.fields['parent'].label = ''
self.fields['parent'].required = False
class Meta():
model = Comment
fields = ('name', 'parent', 'email', 'body')
widgets = {
'name': forms.TextInput(attrs={'class': 'col-sm-12'}),
'email': forms.TextInput(attrs={'class': 'col-sm-12'}),
'body': forms.Textarea(attrs={'class': 'form-control'}),
}
def save(self, *args, **kwargs):
Comment.objects.rebuild()
return super(CommentForm, self).save(*args, **kwargs)
post_details.html:
{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% load mptt_tags%}
{% block content %}
<header class="masthead" style="height: 13.9em;">
<!-- javascript add -->
<script src="{% static 'js/main.js'%}"></script>
</header>
<div class="container">
<div class="row">
<div class="col-md-8 card mb-4 mt-3 left top">
<div class="card-body">
<h1>{% block title %} {{ post.title }} {% endblock title %}</h1>
<p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
<p class="card-text ">{{ post.content | safe }}</p><br>
<a href="{% url 'home' %}" style="float: right;"
class="btn btn-danger">Go Back</a><br><br>
</div>
</div>
{% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}
</div>
<div class="row">
<div class="col-md-8 card mb-4 mt-3 left top">
<div class="card-body">
<h3>Add Comment</h3>
<form method="POST" action="">
{% csrf_token %}
{{ comment_form | crispy }}
<button type="submit" class="btn btn-danger">Comment</button>
</form>
<!-- count comments section-->
{% with comments.count as total_comments %}
<h3 class="mt-5">
{{ total_comments }} comment{{ total_comments|pluralize }}
</h3>
{% endwith %}
<!-- show comment section -->
<div>
<ul style="list-style-type: none;">
{% recursetree comments %}
<li>
<!-- parent body -->
<div>
<div class="border p-4 rounded">
<h4>{{ node.name }}</h4>
<small class="text-muted">On {{ node.created_on }}</small>
<h5>{{ node.body }}</h5> <br>
<button class="btn btn-danger btn-sm" onclick="handleReply({{node.id}})">Reply</button>
<div id="reply-form-container-{{node.id}}" style="display:none">
<form method="POST" action="{% url 'reply' %}" class="mt-3">
{% csrf_token %}
<input type="hidden" name="post_id" value="{{post.id}}">
<input type="hidden" name="parent" value="{{comment.id}}">
<input type="hidden" name="post_url" value="{{post.get_absolute_url}}">
{{comment_form|crispy}}
<div>
<button type="button" onclick="handleCancel({{node.id}})" class="btn btn-danger border btn-sm">Cancel</button>
<button type="submit" class="btn btn-danger btn-sm">Submit</button>
</div>
</form>
</div>
</div>
</div>
<br>
<!-- if children -->
{% if not node.is_leaf_node %}
<ul ul style="list-style-type: none;">
<div class="children pl-2 pl-md-5">
{{ children }}
</div>
</ul>
{% endif %}
</li>
{% endrecursetree%}
</ul>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
I've tried to change the values in the formfields of the post_details.html to node.parent.id
but nothing seems to happen.
Also i tried to change the parent_id's datatype to see if there something happens. but because it runs in this Opperant-Error i think it could be an issue with saving to the mySQL Database...
Upvotes: 0
Views: 161
Reputation: 1
Ok... Now i have have found the answer. In views.py i've edited these lines:
parent_id = request.POST.get('parent')
and replace 'parent' with 'commend_id'
parent_id = request.POST.get('comment_id')
In post_detail.html i have changed the form:
<input type="hidden" name="parent" value="{{comment.id}}">
to...
<input type="hidden" name="comment_id" value="{{node.id}}">
Now it runs as expected! YAY!
Upvotes: 0