7

I have a project with a Post model, that is basic posts. I want to create a link on each post page to be able to delete that post (with appropriate security).

There are a few questions on this on stack overflow, but I can't seem to find a complete, workable answer (I am using Django 1.7) that doesn't throw up errors when I implement it.

I have been able to implement a delete function which works ok, but need to add a POST form with CSRF token for validation, and also check that the user deleting it is the one that created it. I can't seem figure out how to add these two in.

So far, in my views.py:

def delete(request, id):
    post = Post.objects.filter(pk=id).delete()
    return HttpResponseRedirect(reverse('posts.views.all_posts'))

In urls.py:

url(r'^delete/(?P<id>\d+)/$','posts.views.delete'),

In html:

<a href="/delete/{{ post.id }}">Delete</a>

This all works, but there is no security - so appreciate guidance on how to add a form and checking.

Also, I've seen an answer that uses DeleteView, but couldn't get that one to work either.

Pete Drennan
  • 522
  • 1
  • 6
  • 15

1 Answers1

11

Indeed, using a GET method to delete your objects makes you vulnerable to CSRF attacks.

DeleteView only deletes on POST, and shows a confirmation page on GET.

Your code should look something like this in views.py:

from django.views.generic import DeleteView

class PostDelete(DeleteView):
    model = Post
    success_url = reverse_lazy('posts.views.all_posts')

In urls.py:

url(r'^delete/(?P<pk>\d+)/$', PostDelete.as_view(),
        name='entry_delete'),

Your form (without using a confirmation template. There is an example of confirmation template in the docs):

<form action="{% url 'entry_delete' object.pk %}" method="post">
    {% csrf_token %}
    <input type="submit" value="Delete" />
</form>

If you are not using a confirmation template, make sure to point the form's action attribute to the DeleteView (this is why).

To ensure the user deleting the post is the user that owns it, I like to use mixins. Assuming your Post model has a created_by foreign key pointing to User, you could write a mixin like:

from django.core.exceptions import PermissionDenied

class PermissionMixin(object):

    def get_object(self, *args, **kwargs):
        obj = super(PermissionMixin, self).get_object(*args, **kwargs)
        if not obj.created_by == self.request.user:
            raise PermissionDenied()
        else:
            return obj

Finally, your DeleteView should inherit from this mixin:

class PostDelete(PermissionMixin, DeleteView):

    model = Post
    success_url = reverse_lazy('posts.views.all_posts')
Community
  • 1
  • 1
Aylen
  • 3,524
  • 26
  • 36
  • Brilliant, that worked perfectly - I tried it without the confirmation screen and it works really well. I'll try now to add in the confirmation screen. – Pete Drennan Oct 02 '14 at 06:48
  • For anyone looking at this in the future, you also need to import PostDelete in your urls.py eg: from views import PostDelete – Pete Drennan Oct 02 '14 at 06:50