'Django comments deletion
I am having challenges in getting user comment deletion functionality for my django blog. The logic is as follows:
- user who made a comment under blog post and is logged in will see Delete button next to their comment
- once the user click on delete comment button they are taken to Comment_confirm_delete page to confirm deletion
Below is what I have:
Models.py
from django.db import models
from django.contrib.auth.models import User
from cloudinary.models import CloudinaryField
STATUS = ((0, "Draft"), (1, "Published"))
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()
featured_image = CloudinaryField('image', default='placeholder')
excerpt = models.TextField(blank=True)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, related_name='blog_likes', blank=True)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
class Meta:
ordering = ['-created_on']
def __str__(self):
return f"Comment {self.body} by {self.name}"
def get_absolute_url(self):
return reverse('post-detail', kwargs={'comment': id})
urls.py
from . import views
from .views import CommentDeleteView
from django.urls import path
urlpatterns = [
path("", views.PostList.as_view(), name="home"),
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
path('like/<slug:slug>', views.PostLike.as_view(), name='post_like'),
path('delete/<comment_id>', views.CommentDeleteView.as_view(), name='comment_confirm_delete')
]
views.py
from django.shortcuts import render, get_object_or_404, reverse
from django.urls import reverse_lazy
from django.views.generic.edit import DeleteView
from django.views import generic, View
from django.http import HttpResponseRedirect
from .models import Post, Comment
from .forms import CommentForm
class PostList(generic.ListView):
model = Post
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
paginate_by = 4
class PostDetail(View):
def get(self, request, slug, *args, **kwargs):
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.filter(approved=True).order_by("-created_on")
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"post_detail.html",
{
"post": post,
"comments": comments,
"commented": False,
"liked": liked,
"comment_form": CommentForm()
},
)
def post(self, request, slug, *args, **kwargs):
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.filter(approved=True).order_by("-created_on")
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment_form.instance.email = request.user.email
comment_form.instance.name = request.user.username
comment = comment_form.save(commit=False)
comment.post = post
comment.save()
else:
comment_form = CommentForm()
return render(
request,
"post_detail.html",
{
"post": post,
"comments": comments,
"commented": True,
"liked": liked,
"comment_form": CommentForm()
},
)
class PostLike(View):
def post(self, request, slug):
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('post_detail', args=[slug]))
class CommentDeleteView(DeleteView):
model = Comment
template_name = 'comment_confirm_delete.html'
success_url = "/"
def test_func(self, comment_id):
comment = self.get_object()
if self.request.user == comment.name:
return render(request, "comment_confirm_delete.html")
else:
return HttpResponseRedirect(reverse('post_detail', args=[slug]))
post_detail.html
<!-- The body of the comment goes before the | -->
{{ comment.body | linebreaks }}
{% if user.username == comment.name %}
<a href="{% url 'comment_confirm_delete' comment.id %}"i class="fas fa-trash">
Delete</a>
I am looking for advice and specific comments as to what needs to be corrected in terms of the structure. At the moment I am getting error : Generic detail view CommentDeleteView must be called with either an object pk or a slug in the URLconf.
many thanks
Solution 1:[1]
Change your comment deletion url path to:
path('delete/<int:pk>', views.CommentDeleteView.as_view(), name='comment_confirm_delete')
and correct your test_func method:
class CommentDeleteView(DeleteView):
...
def test_func(self):
comment = self.get_object()
if self.request.user.username == comment.name:
return True
return False
PS think about storing User that commented with ForeignKey, because if that user would change his username and some other user would change to the first one's, then the other user will be able to delete it. Charfield is definitely bad approach to that.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
