I looked through similar questions on the same topic and I think I am following all the rules specified for has_object_permission
.
This is what I have in my settings.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
'users.permissions.CanAccessData', # this is my custom class
],
...
}
This is my permission class
class CanAccessData(permissions.BasePermission):
message = 'You do not have permission to perform this action.'
def has_permission(self, request, view):
print "has_permission`"
return True
def has_object_permission(self, request, view, obj):
print "has_object_permission"
return False
Here is my view structure:
class CompleteList(generics.ListCreateAPIView):
permission_classes = (CanAccessData,)
serializer_class = SomeSerializer
model = Some
filter_backends = (filters.OrderingFilter, filters.SearchFilter)
ordering_fields = (tuple of Some fields)
search_fields = ordering_fields
ordering = ('-create_date')
Still, has_object_permission
is not getting called, has_permission
gets called though.
The has_object_permission
is not called for list views. The documentation says the following:
Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the filtering documentation for more details.
I ran into the same problem. The has_object_permission
function is never called when listing objects.
Even if the following may not be the most efficient solution, you can override the list
method in your view as follows, which is how I solved it for me:
from typing import List
import rest_framework.permissions as drf_permissions
def list(self, request, *args, **kwargs):
# base query set
queryset: QuerySet = self.filter_queryset(self.get_queryset())
# check object permissions for each object individually
valid_pks: List[int] = [] # # storage for keys of valid objects
permissions: List[drf_permissions.BasePermission] = self.get_permissions()
for obj in queryset:
for permission in permissions:
if permission.has_object_permission(request, self, obj):
valid_pks.append(obj.pk)
# remove not valid objects from the queryset
queryset = queryset.filter(pk__in=valid_pks)
# ... business as usual (original code)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
This basically is the original implementation except that it prefetches the relevant objects and checks the permission individually.
However, this may be DRY in the sense that you don't have to override the get_queryset()
method to somehow reinvent your has_object_permission
logic. But also it is slow, since it fetches objects twice. You could improve that situation by working with the already prefetched objects instead of filtering the queryset, though.
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