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)