Skip to content

Commit db37512

Browse files
authored
Remove 3.10 deprecations (#6687)
* Remove DjangoObjectPermissionsFilter * Remove detail_route/list_route * Bump deprecation warning versions
1 parent ccd9b71 commit db37512

File tree

7 files changed

+15
-187
lines changed

7 files changed

+15
-187
lines changed

rest_framework/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
2626

2727

28-
class RemovedInDRF310Warning(DeprecationWarning):
28+
class RemovedInDRF311Warning(DeprecationWarning):
2929
pass
3030

3131

32-
class RemovedInDRF311Warning(PendingDeprecationWarning):
32+
class RemovedInDRF312Warning(PendingDeprecationWarning):
3333
pass

rest_framework/compat.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,6 @@ def distinct(queryset, base):
131131
requests = None
132132

133133

134-
def is_guardian_installed():
135-
"""
136-
django-guardian is optional and only imported if in INSTALLED_APPS.
137-
"""
138-
return 'guardian' in settings.INSTALLED_APPS
139-
140-
141134
# PATCH method is not implemented by Django
142135
if 'patch' not in View.http_method_names:
143136
View.http_method_names = View.http_method_names + ['patch']

rest_framework/decorators.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
used to annotate methods on viewsets that should be included by routers.
88
"""
99
import types
10-
import warnings
1110

1211
from django.forms.utils import pretty_name
1312

14-
from rest_framework import RemovedInDRF310Warning
1513
from rest_framework.views import APIView
1614

1715

@@ -214,39 +212,3 @@ def options(self, func):
214212

215213
def trace(self, func):
216214
return self._map('trace', func)
217-
218-
219-
def detail_route(methods=None, **kwargs):
220-
"""
221-
Used to mark a method on a ViewSet that should be routed for detail requests.
222-
"""
223-
warnings.warn(
224-
"`detail_route` is deprecated and will be removed in 3.10 in favor of "
225-
"`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.",
226-
RemovedInDRF310Warning, stacklevel=2
227-
)
228-
229-
def decorator(func):
230-
func = action(methods, detail=True, **kwargs)(func)
231-
if 'url_name' not in kwargs:
232-
func.url_name = func.url_path.replace('_', '-')
233-
return func
234-
return decorator
235-
236-
237-
def list_route(methods=None, **kwargs):
238-
"""
239-
Used to mark a method on a ViewSet that should be routed for list requests.
240-
"""
241-
warnings.warn(
242-
"`list_route` is deprecated and will be removed in 3.10 in favor of "
243-
"`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.",
244-
RemovedInDRF310Warning, stacklevel=2
245-
)
246-
247-
def decorator(func):
248-
func = action(methods, detail=False, **kwargs)(func)
249-
if 'url_name' not in kwargs:
250-
func.url_name = func.url_path.replace('_', '-')
251-
return func
252-
return decorator

rest_framework/filters.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
returned by list views.
44
"""
55
import operator
6-
import warnings
76
from functools import reduce
87

98
from django.core.exceptions import ImproperlyConfigured
@@ -14,10 +13,7 @@
1413
from django.utils.encoding import force_text
1514
from django.utils.translation import gettext_lazy as _
1615

17-
from rest_framework import RemovedInDRF310Warning
18-
from rest_framework.compat import (
19-
coreapi, coreschema, distinct, is_guardian_installed
20-
)
16+
from rest_framework.compat import coreapi, coreschema, distinct
2117
from rest_framework.settings import api_settings
2218

2319

@@ -315,41 +311,3 @@ def get_schema_operation_parameters(self, view):
315311
},
316312
},
317313
]
318-
319-
320-
class DjangoObjectPermissionsFilter(BaseFilterBackend):
321-
"""
322-
A filter backend that limits results to those where the requesting user
323-
has read object level permissions.
324-
"""
325-
def __init__(self):
326-
warnings.warn(
327-
"`DjangoObjectPermissionsFilter` has been deprecated and moved to "
328-
"the 3rd-party django-rest-framework-guardian package.",
329-
RemovedInDRF310Warning, stacklevel=2
330-
)
331-
assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'
332-
333-
perm_format = '%(app_label)s.view_%(model_name)s'
334-
335-
def filter_queryset(self, request, queryset, view):
336-
# We want to defer this import until run-time, rather than import-time.
337-
# See https://github.com/encode/django-rest-framework/issues/4608
338-
# (Also see #1624 for why we need to make this import explicitly)
339-
from guardian import VERSION as guardian_version
340-
from guardian.shortcuts import get_objects_for_user
341-
342-
extra = {}
343-
user = request.user
344-
model_cls = queryset.model
345-
kwargs = {
346-
'app_label': model_cls._meta.app_label,
347-
'model_name': model_cls._meta.model_name
348-
}
349-
permission = self.perm_format % kwargs
350-
if tuple(guardian_version) >= (1, 3):
351-
# Maintain behavior compatibility with versions prior to 1.3
352-
extra = {'accept_global_perms': False}
353-
else:
354-
extra = {}
355-
return get_objects_for_user(user, permission, queryset, **extra)

rest_framework/routers.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
from django.urls import NoReverseMatch
2323
from django.utils.deprecation import RenameMethodsBase
2424

25-
from rest_framework import (
26-
RemovedInDRF310Warning, RemovedInDRF311Warning, views
27-
)
25+
from rest_framework import RemovedInDRF311Warning, views
2826
from rest_framework.response import Response
2927
from rest_framework.reverse import reverse
3028
from rest_framework.schemas import SchemaGenerator
@@ -36,28 +34,6 @@
3634
DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs'])
3735

3836

39-
class DynamicDetailRoute:
40-
def __new__(cls, url, name, initkwargs):
41-
warnings.warn(
42-
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
43-
"in favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
44-
"`DynamicRoute(url, name, True, initkwargs)` instead.",
45-
RemovedInDRF310Warning, stacklevel=2
46-
)
47-
return DynamicRoute(url, name, True, initkwargs)
48-
49-
50-
class DynamicListRoute:
51-
def __new__(cls, url, name, initkwargs):
52-
warnings.warn(
53-
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
54-
"favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
55-
"`DynamicRoute(url, name, False, initkwargs)` instead.",
56-
RemovedInDRF310Warning, stacklevel=2
57-
)
58-
return DynamicRoute(url, name, False, initkwargs)
59-
60-
6137
def escape_curly_brackets(url_path):
6238
"""
6339
Double brackets in regex of url_path for escape string formatting

tests/test_decorators.py

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import pytest
22
from django.test import TestCase
33

4-
from rest_framework import RemovedInDRF310Warning, status
4+
from rest_framework import status
55
from rest_framework.authentication import BasicAuthentication
66
from rest_framework.decorators import (
7-
action, api_view, authentication_classes, detail_route, list_route,
8-
parser_classes, permission_classes, renderer_classes, schema,
9-
throttle_classes
7+
action, api_view, authentication_classes, parser_classes,
8+
permission_classes, renderer_classes, schema, throttle_classes
109
)
1110
from rest_framework.parsers import JSONParser
1211
from rest_framework.permissions import IsAuthenticated
@@ -285,39 +284,3 @@ def test_action():
285284
@test_action.mapping.post
286285
def test_action():
287286
raise NotImplementedError
288-
289-
def test_detail_route_deprecation(self):
290-
with pytest.warns(RemovedInDRF310Warning) as record:
291-
@detail_route()
292-
def view(request):
293-
raise NotImplementedError
294-
295-
assert len(record) == 1
296-
assert str(record[0].message) == (
297-
"`detail_route` is deprecated and will be removed in "
298-
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
299-
"`@action(detail=True)` instead."
300-
)
301-
302-
def test_list_route_deprecation(self):
303-
with pytest.warns(RemovedInDRF310Warning) as record:
304-
@list_route()
305-
def view(request):
306-
raise NotImplementedError
307-
308-
assert len(record) == 1
309-
assert str(record[0].message) == (
310-
"`list_route` is deprecated and will be removed in "
311-
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
312-
"`@action(detail=False)` instead."
313-
)
314-
315-
def test_route_url_name_from_path(self):
316-
# pre-3.8 behavior was to base the `url_name` off of the `url_path`
317-
with pytest.warns(RemovedInDRF310Warning):
318-
@list_route(url_path='foo_bar')
319-
def view(request):
320-
raise NotImplementedError
321-
322-
assert view.url_path == 'foo_bar'
323-
assert view.url_name == 'foo-bar'

tests/test_permissions.py

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import base64
22
import unittest
3-
import warnings
43
from unittest import mock
54

65
import django
76
import pytest
7+
from django.conf import settings
88
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
99
from django.db import models
1010
from django.test import TestCase
1111
from django.urls import ResolverMatch
1212

1313
from rest_framework import (
14-
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
15-
permissions, serializers, status, views
14+
HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers,
15+
status, views
1616
)
17-
from rest_framework.compat import PY36, is_guardian_installed
18-
from rest_framework.filters import DjangoObjectPermissionsFilter
17+
from rest_framework.compat import PY36
1918
from rest_framework.routers import DefaultRouter
2019
from rest_framework.test import APIRequestFactory
2120
from tests.models import BasicModel
@@ -309,7 +308,7 @@ def get_queryset(self):
309308
get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view()
310309

311310

312-
@unittest.skipUnless(is_guardian_installed(), 'django-guardian not installed')
311+
@unittest.skipUnless('guardian' in settings.INSTALLED_APPS, 'django-guardian not installed')
313312
class ObjectPermissionsIntegrationTests(TestCase):
314313
"""
315314
Integration tests for the object level permissions API.
@@ -418,37 +417,14 @@ def test_can_read_get_queryset_permissions(self):
418417
self.assertEqual(response.status_code, status.HTTP_200_OK)
419418

420419
# Read list
421-
def test_django_object_permissions_filter_deprecated(self):
422-
with warnings.catch_warnings(record=True) as w:
423-
warnings.simplefilter("always")
424-
DjangoObjectPermissionsFilter()
425-
426-
message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved "
427-
"to the 3rd-party django-rest-framework-guardian package.")
428-
self.assertEqual(len(w), 1)
429-
self.assertIs(w[-1].category, RemovedInDRF310Warning)
430-
self.assertEqual(str(w[-1].message), message)
431-
420+
# Note: this previously tested `DjangoObjectPermissionsFilter`, which has
421+
# since been moved to a separate package. These now act as sanity checks.
432422
def test_can_read_list_permissions(self):
433423
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['readonly'])
434-
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
435-
# TODO: remove in version 3.10
436-
with warnings.catch_warnings(record=True):
437-
warnings.simplefilter("always")
438-
response = object_permissions_list_view(request)
424+
response = object_permissions_list_view(request)
439425
self.assertEqual(response.status_code, status.HTTP_200_OK)
440426
self.assertEqual(response.data[0].get('id'), 1)
441427

442-
def test_cannot_read_list_permissions(self):
443-
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['writeonly'])
444-
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
445-
# TODO: remove in version 3.10
446-
with warnings.catch_warnings(record=True):
447-
warnings.simplefilter("always")
448-
response = object_permissions_list_view(request)
449-
self.assertEqual(response.status_code, status.HTTP_200_OK)
450-
self.assertListEqual(response.data, [])
451-
452428
def test_cannot_method_not_allowed(self):
453429
request = factory.generic('METHOD_NOT_ALLOWED', '/', HTTP_AUTHORIZATION=self.credentials['readonly'])
454430
response = object_permissions_list_view(request)

0 commit comments

Comments
 (0)