I have searched through the other questions similar to my own problem and have come to no solution so im hoping someone can help me figure out where i went wrong.
I'm trying to implement a delete post option in my blog program but it is throwing the following error once you click the 'delete' button:
ImproperlyConfigured at /18/delete/ Deletepost is missing a QuerySet. Define Deletepost.model, Deletepost.queryset, or override Deletepost.get_queryset().
I am nearly sure its a problem with my URLS.py though what exactly i cannot figure out.
the following is the code in question:
Views.py
# delete post
class Deletepost(LoginRequiredMixin, DeleteView):
form_class = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
urls.py
urlpatterns = [
# home
path('', views.postslist.as_view(), name='home'),
# add post
path('blog_post/', views.PostCreateView.as_view(), name='blog_post'),
# posts/comments
path('<slug:slug>/', views.postdetail.as_view(), name='post_detail'),
# edit post
path('<slug:slug>/edit/', views.Editpost.as_view(), name='edit_post'),
# delete post
path('<int:pk>/delete/', views.Deletepost.as_view(), name='delete_post'),
# likes
path('like/<slug:slug>', views.PostLike.as_view(), name='post_like'),
]
post.html
{% extends 'base.html' %} {% block content %}
{% load crispy_forms_tags %}
<div >
<div >
<div >
<div >
<!-- Post title goes in these h1 tags -->
<h1 >{{ post.title }}</h1>
<!-- Post author goes before the | the post's created date goes after -->
<p >{{ post.author }} | {{ post.created_on }}</p>
</div>
<div >
<!-- The featured image URL goes in the src attribute -->
{% if "placeholder" in post.featured_image.url %}
<img src="https://codeinstitute.s3.amazonaws.com/fullstack/blog/default.jpg" width="100%">
{% else %}
<img src=" {{ post.featured_image.url }}" width="100%">
{% endif %}
</div>
</div>
</div>
</div>
<div >
<div >
<div >
<div >
<!-- The post content goes inside the card-text. -->
<!-- Use the | safe filter inside the template tags -->
<p >
{{ post.content | safe }}
</p>
<div >
<div >
<strong>
{% if user.is_authenticated %}
<form action="{% url 'post_like' post.slug %}" method="POST">
{% csrf_token %}
{% if liked %}
<button type="submit" name="blogpost_id" value="{{post.slug}}" ><i ></i></button>
{% else %}
<button type="submit" name="blogpost_id" value="{{post.slug}}" ><i ></i></button>
{% endif %}
</form>
{% else %}
<span ><i ></i></span>
{% endif %}
<!-- The number of likes goes before the closing strong tag -->
<span >{{ post.number_of_likes }} </span>
</strong>
</div>
<div >
{% with comments.count as total_comments %}
<strong ><i ></i>
<!-- Our total_comments variable goes before the closing strong tag -->
{{ total_comments }}</strong>
{% endwith %}
</div>
<div >
<a href="{% url 'delete_post' post.id %}">Delete It</a>
</div>
</div>
</div>
</div>
</div>
<div >
<div >
<hr>
</div>
</div>
<div >
<div >
<h3 >Comments:</h3>
<div >
<!-- We want a for loop inside the empty control tags to iterate through each comment in comments -->
{% for comment in comments %}
<div style="padding: 10px;">
<p >
<!-- The commenter's name goes here. Check the model if you're not sure what that is -->
{{ comment.name }}
<span >
<!-- The comment's created date goes here -->
{{ comment.created_on }}
</span> wrote:
</p>
<!-- The body of the comment goes before the | -->
{{ comment.body | linebreaks }}
</div>
<!-- Our for loop ends here -->
{% endfor %}
</div>
</div>
<div >
<div >
<!-- For later -->
{% if commented %}
<div role="alert">
Your comment is awaiting approval
</div>
{% else %}
{% if user.is_authenticated %}
<h3 >Leave a comment:</h3>
<p >Posting as: {{ user.username }}</p>
<form method="post" style="margin-top: 1.3em;">
{{ comment_form | crispy }}
{% csrf_token %}
<button type="submit" >Submit</button>
</form>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
{% endblock content %}
Any ideas?
CodePudding user response:
I think it should be model
not form_class
so:
class Deletepost(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
CodePudding user response:
@SunderamDubey's answer is correct. The test_func
will however not run, since this is method of the UserPassesTestMixin
[Django-doc], not LoginRequiredMixin
.
But using a test function as is done here is not efficient: it will fetch the same object twice from the database. You can simply restrict the queryset, like:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import DeleteView
class DeletePostView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('blog:home')
template_name = 'templates/post.html'
def get_queryset(self, *args, **kwargs):
return (
super().get_queryset(*args, **kwargs).filter(author=self.request.user)
)
This will do filtering at the database side, and thus return a 404 in case the logged in user is not the author of the Post
object you want to delete.
In the template, you will need to make a mini-form to make a POST request, for example:
<form method="post" action="{% url 'delete_post' post.id %}"> {% csrf_token %} <button type="submit">Delete</button> </form>
CodePudding user response:
In my opinion, you should change url to below 'path('delete/int:pk', views.Deletepost.as_view(), name='delete_post'),'
if didn't work you can do this
def delete_post(request, post_id):
post = Post.objects.get(pk=post_id)
post.delete()
return redirect('blog:home')