diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 8731cab08d..5748813784 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -150,7 +150,7 @@ Similar to `DjangoModelPermissions`, but also allows unauthenticated users to ha This permission class ties into Django's standard [object permissions framework][objectpermissions] that allows per-object permissions on models. In order to use this permission class, you'll also need to add a permission backend that supports object-level permissions, such as [django-guardian][guardian]. -As with `DjangoModelPermissions`, this permission must only be applied to views that have a `.queryset` property. Authorization will only be granted if the user *is authenticated* and has the *relevant per-object permissions* and *relevant model permissions* assigned. +As with `DjangoModelPermissions`, this permission must only be applied to views that have a `.queryset` property or `.get_queryset()` method. Authorization will only be granted if the user *is authenticated* and has the *relevant per-object permissions* and *relevant model permissions* assigned. * `POST` requests require the user to have the `add` permission on the model instance. * `PUT` and `PATCH` requests require the user to have the `change` permission on the model instance. diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 8fa7e44523..a8d714718a 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -3,6 +3,7 @@ """ from __future__ import unicode_literals from django.http import Http404 +from django.utils import six from rest_framework.compat import get_model_name SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') @@ -109,8 +110,6 @@ def get_required_permissions(self, method, model_cls): def has_permission(self, request, view): try: queryset = view.get_queryset() - except AttributeError: - queryset = getattr(view, 'queryset', None) except AssertionError: # view.get_queryset() didn't find .queryset queryset = None @@ -172,7 +171,14 @@ def get_required_object_permissions(self, method, model_cls): return [perm % kwargs for perm in self.perms_map[method]] def has_object_permission(self, request, view, obj): - model_cls = view.queryset.model + try: + queryset = view.get_queryset() + except AssertionError as exc: + # view.get_queryset() didn't find .queryset + raise six.raise_from(AssertionError( + 'Cannot apply DjangoObjectPermissions on a view that ' + 'does not have `.queryset` property nor redefines `.get_queryset()`.'), exc) + model_cls = queryset.model user = request.user perms = self.get_required_object_permissions(request.method, model_cls) diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 9225308c7f..4a8a8ee8e2 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -227,6 +227,18 @@ class ObjectPermissionListView(generics.ListAPIView): object_permissions_list_view = ObjectPermissionListView.as_view() +class GetQuerysetObjectPermissionInstanceView(generics.RetrieveUpdateDestroyAPIView): + serializer_class = BasicPermSerializer + authentication_classes = [authentication.BasicAuthentication] + permission_classes = [ViewObjectPermissions] + + def get_queryset(self): + return BasicPermModel.objects.all() + + +get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view() + + @unittest.skipUnless(guardian, 'django-guardian not installed') class ObjectPermissionsIntegrationTests(TestCase): """ @@ -326,6 +338,15 @@ def test_cannot_read_permissions(self): response = object_permissions_view(request, pk='1') self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + def test_can_read_get_queryset_permissions(self): + """ + same as ``test_can_read_permissions`` but with a view + that rely on ``.get_queryset()`` instead of ``.queryset``. + """ + request = factory.get('/1', HTTP_AUTHORIZATION=self.credentials['readonly']) + response = get_queryset_object_permissions_view(request, pk='1') + self.assertEqual(response.status_code, status.HTTP_200_OK) + # Read list def test_can_read_list_permissions(self): request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['readonly'])
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: