'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