Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delete objects in Django via html link

Tags:

django

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.

like image 394
Pete Drennan Avatar asked Oct 02 '14 02:10

Pete Drennan


1 Answers

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')
like image 160
Aylen Avatar answered Oct 21 '22 05:10

Aylen