I have a model named Post
and have a field there called owner
(foreign key to User
). Of course, only owners can update
or delete
their own posts.
That being said, I use login_required
decorator in the views to make sure the user is logged in but then, I also need to make sure the user trying to update
/delete
the question is the owner
.
As I'm using Django: Generic Editing Views the documentation says I need to use Django: UserPassesTestMixin.
This validation will be done for the update
and delete
views. DRY, what is the way to go about this? should I create a class named TestUserOwnerOfPost
and create a test_func()
and then make the update
and delete
views inherit from it?
Cause that's what I have tried and didn't work, code below:
from django.views.generic.edit import UpdateView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import UserPassesTestMixin
class TestUserOwnerOfPost(UserPassesTestMixin):
def test_func(self):
return self.request.user == self.post.owner
class EditPost(UpdateView, TestUserOwnerOfPost):
model = Post
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(EditPost, self).dispatch(*args, **kwargs)
With the code above, every logged-in user in the system can edit
/delete
any post. What am I doing wrong? am I missing something? thanks.
The first problem is that the order of the classes you inherit is incorrect, as @rafalmp says.
However, fixing that doesn't solve the problem, because the UserPassesTest
mixin performs the test before running the view. This means that it's not really suitable to check the owner of self.object
, because self.object
has not been set yet. Note I'm using self.object
instead of self.post
-- I'm don't think that the view ever sets self.post
but I might be wrong about that.
One option is to call self.get_object()
inside the test function. This is a bit inefficient because your view will fetch the object twice, but in practice it probably doesn't matter.
def test_func(self):
self.object = self.get_object()
return self.request.user == self.object.owner
Another approach is to override get_queryset
, to restrict it to objects owned by the user. This means the user will get a 404 error if they do not own the object. This behaviour is not exactly the same as the UserPassesTestMixin
, which will redirect to a login page, but it might be ok for you.
class OwnerQuerysetMixin(object):
def get_queryset(self):
queryset = super(OwnerQuerysetMixin, self).get_queryset()
# perhaps handle the case where user is not authenticated
queryset = queryset.filter(owner=self.request.user)
return queryset
The order of the classes you inherit from matters. For your access control to work, it must be enforced before UpdateView is executed:
class EditPost(TestUserOwnerOfPost, UpdateView):
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With