Skip to content

Commit e5eb315

Browse files
author
Ryan P Kilby
committed
Unify QS handling for model/object permissions
1 parent af460d2 commit e5eb315

File tree

2 files changed

+36
-24
lines changed

2 files changed

+36
-24
lines changed

rest_framework/permissions.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ def get_required_permissions(self, method, model_cls):
114114

115115
return [perm % kwargs for perm in self.perms_map[method]]
116116

117+
def _queryset(self, view):
118+
assert hasattr(view, 'get_queryset') \
119+
or getattr(view, 'queryset', None) is not None, (
120+
'Cannot apply {} on a view that does not set '
121+
'`.queryset` or have a `.get_queryset()` method.'
122+
).format(self.__class__.__name__)
123+
124+
if hasattr(view, 'get_queryset'):
125+
return view.get_queryset()
126+
return view.queryset
127+
117128
def has_permission(self, request, view):
118129
# Workaround to ensure DjangoModelPermissions are not applied
119130
# to the root view when using DefaultRouter.
@@ -124,19 +135,7 @@ def has_permission(self, request, view):
124135
not is_authenticated(request.user) and self.authenticated_users_only):
125136
return False
126137

127-
if hasattr(view, 'get_queryset'):
128-
queryset = view.get_queryset()
129-
assert queryset is not None, (
130-
'{}.get_queryset() returned None'.format(view.__class__.__name__)
131-
)
132-
else:
133-
queryset = getattr(view, 'queryset', None)
134-
135-
assert queryset is not None, (
136-
'Cannot apply DjangoModelPermissions on a view that '
137-
'does not set `.queryset` or have a `.get_queryset()` method.'
138-
)
139-
138+
queryset = self._queryset(view)
140139
perms = self.get_required_permissions(request.method, queryset.model)
141140

142141
return request.user.has_perms(perms)
@@ -183,16 +182,8 @@ def get_required_object_permissions(self, method, model_cls):
183182
return [perm % kwargs for perm in self.perms_map[method]]
184183

185184
def has_object_permission(self, request, view, obj):
186-
if hasattr(view, 'get_queryset'):
187-
queryset = view.get_queryset()
188-
else:
189-
queryset = getattr(view, 'queryset', None)
190-
191-
assert queryset is not None, (
192-
'Cannot apply DjangoObjectPermissions on a view that '
193-
'does not set `.queryset` or have a `.get_queryset()` method.'
194-
)
195-
185+
# authentication checks have already executed via has_permission
186+
queryset = self._queryset(view)
196187
model_cls = queryset.model
197188
user = request.user
198189

tests/test_permissions.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from rest_framework import (
1111
HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers,
12-
status
12+
status, views
1313
)
1414
from rest_framework.compat import ResolverMatch, guardian, set_many
1515
from rest_framework.filters import DjangoObjectPermissionsFilter
@@ -219,6 +219,27 @@ def get_queryset(_):
219219
response = view(request)
220220
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
221221

222+
def test_queryset_assertions(self):
223+
class View(views.APIView):
224+
authentication_classes = [authentication.BasicAuthentication]
225+
permission_classes = [permissions.DjangoModelPermissions]
226+
view = View.as_view()
227+
228+
request = factory.get('/', HTTP_AUTHORIZATION=self.permitted_credentials)
229+
msg = 'Cannot apply DjangoModelPermissions on a view that does not set `.queryset` or have a `.get_queryset()` method.'
230+
with self.assertRaisesMessage(AssertionError, msg):
231+
view(request)
232+
233+
# Faulty `get_queryset()` methods should trigger the above "view does not have a queryset" assertion.
234+
class View(RootView):
235+
def get_queryset(self):
236+
return None
237+
view = View.as_view()
238+
239+
request = factory.get('/', HTTP_AUTHORIZATION=self.permitted_credentials)
240+
with self.assertRaisesMessage(AttributeError, "'NoneType' object has no attribute 'model'"):
241+
view(request)
242+
222243

223244
class BasicPermModel(models.Model):
224245
text = models.CharField(max_length=100)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy