From 882f03789cc45c520493320cefa51555e0122fdb Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 22 Apr 2015 12:35:09 +0200 Subject: [PATCH 1/2] Allow DjangoObjectPermissions to use views that define get_queryset --- rest_framework/permissions.py | 2 +- tests/test_permissions.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 8fa7e4452..95b538429 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -172,7 +172,7 @@ class DjangoObjectPermissions(DjangoModelPermissions): return [perm % kwargs for perm in self.perms_map[method]] def has_object_permission(self, request, view, obj): - model_cls = view.queryset.model + model_cls = view.get_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 9225308c7..4a8a8ee8e 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 @@ class ObjectPermissionsIntegrationTests(TestCase): 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']) From 76bc42ce97fb1ab491bc69b307ae555685e414b8 Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Mon, 27 Apr 2015 09:16:43 +0200 Subject: [PATCH 2/2] Reflect modification on documentation --- docs/api-guide/permissions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 8731cab08..574881378 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.