Remove 3.10 deprecations (#6687)

* Remove DjangoObjectPermissionsFilter
* Remove detail_route/list_route
* Bump deprecation warning versions
This commit is contained in:
Ryan P Kilby 2019-05-21 10:36:55 -07:00 committed by GitHub
parent ccd9b71c0a
commit db37512a6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 15 additions and 187 deletions

View File

@ -25,9 +25,9 @@ ISO_8601 = 'iso-8601'
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
class RemovedInDRF310Warning(DeprecationWarning):
class RemovedInDRF311Warning(DeprecationWarning):
pass
class RemovedInDRF311Warning(PendingDeprecationWarning):
class RemovedInDRF312Warning(PendingDeprecationWarning):
pass

View File

@ -131,13 +131,6 @@ except ImportError:
requests = None
def is_guardian_installed():
"""
django-guardian is optional and only imported if in INSTALLED_APPS.
"""
return 'guardian' in settings.INSTALLED_APPS
# PATCH method is not implemented by Django
if 'patch' not in View.http_method_names:
View.http_method_names = View.http_method_names + ['patch']

View File

@ -7,11 +7,9 @@ based views, as well as the `@detail_route` and `@list_route` decorators, which
used to annotate methods on viewsets that should be included by routers.
"""
import types
import warnings
from django.forms.utils import pretty_name
from rest_framework import RemovedInDRF310Warning
from rest_framework.views import APIView
@ -214,39 +212,3 @@ class MethodMapper(dict):
def trace(self, func):
return self._map('trace', func)
def detail_route(methods=None, **kwargs):
"""
Used to mark a method on a ViewSet that should be routed for detail requests.
"""
warnings.warn(
"`detail_route` is deprecated and will be removed in 3.10 in favor of "
"`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.",
RemovedInDRF310Warning, stacklevel=2
)
def decorator(func):
func = action(methods, detail=True, **kwargs)(func)
if 'url_name' not in kwargs:
func.url_name = func.url_path.replace('_', '-')
return func
return decorator
def list_route(methods=None, **kwargs):
"""
Used to mark a method on a ViewSet that should be routed for list requests.
"""
warnings.warn(
"`list_route` is deprecated and will be removed in 3.10 in favor of "
"`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.",
RemovedInDRF310Warning, stacklevel=2
)
def decorator(func):
func = action(methods, detail=False, **kwargs)(func)
if 'url_name' not in kwargs:
func.url_name = func.url_path.replace('_', '-')
return func
return decorator

View File

@ -3,7 +3,6 @@ Provides generic filtering backends that can be used to filter the results
returned by list views.
"""
import operator
import warnings
from functools import reduce
from django.core.exceptions import ImproperlyConfigured
@ -14,10 +13,7 @@ from django.template import loader
from django.utils.encoding import force_text
from django.utils.translation import gettext_lazy as _
from rest_framework import RemovedInDRF310Warning
from rest_framework.compat import (
coreapi, coreschema, distinct, is_guardian_installed
)
from rest_framework.compat import coreapi, coreschema, distinct
from rest_framework.settings import api_settings
@ -315,41 +311,3 @@ class OrderingFilter(BaseFilterBackend):
},
},
]
class DjangoObjectPermissionsFilter(BaseFilterBackend):
"""
A filter backend that limits results to those where the requesting user
has read object level permissions.
"""
def __init__(self):
warnings.warn(
"`DjangoObjectPermissionsFilter` has been deprecated and moved to "
"the 3rd-party django-rest-framework-guardian package.",
RemovedInDRF310Warning, stacklevel=2
)
assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'
perm_format = '%(app_label)s.view_%(model_name)s'
def filter_queryset(self, request, queryset, view):
# We want to defer this import until run-time, rather than import-time.
# See https://github.com/encode/django-rest-framework/issues/4608
# (Also see #1624 for why we need to make this import explicitly)
from guardian import VERSION as guardian_version
from guardian.shortcuts import get_objects_for_user
extra = {}
user = request.user
model_cls = queryset.model
kwargs = {
'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.model_name
}
permission = self.perm_format % kwargs
if tuple(guardian_version) >= (1, 3):
# Maintain behavior compatibility with versions prior to 1.3
extra = {'accept_global_perms': False}
else:
extra = {}
return get_objects_for_user(user, permission, queryset, **extra)

View File

@ -22,9 +22,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.urls import NoReverseMatch
from django.utils.deprecation import RenameMethodsBase
from rest_framework import (
RemovedInDRF310Warning, RemovedInDRF311Warning, views
)
from rest_framework import RemovedInDRF311Warning, views
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.schemas import SchemaGenerator
@ -36,28 +34,6 @@ Route = namedtuple('Route', ['url', 'mapping', 'name', 'detail', 'initkwargs'])
DynamicRoute = namedtuple('DynamicRoute', ['url', 'name', 'detail', 'initkwargs'])
class DynamicDetailRoute:
def __new__(cls, url, name, initkwargs):
warnings.warn(
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
"in favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
"`DynamicRoute(url, name, True, initkwargs)` instead.",
RemovedInDRF310Warning, stacklevel=2
)
return DynamicRoute(url, name, True, initkwargs)
class DynamicListRoute:
def __new__(cls, url, name, initkwargs):
warnings.warn(
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
"favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
"`DynamicRoute(url, name, False, initkwargs)` instead.",
RemovedInDRF310Warning, stacklevel=2
)
return DynamicRoute(url, name, False, initkwargs)
def escape_curly_brackets(url_path):
"""
Double brackets in regex of url_path for escape string formatting

View File

@ -1,12 +1,11 @@
import pytest
from django.test import TestCase
from rest_framework import RemovedInDRF310Warning, status
from rest_framework import status
from rest_framework.authentication import BasicAuthentication
from rest_framework.decorators import (
action, api_view, authentication_classes, detail_route, list_route,
parser_classes, permission_classes, renderer_classes, schema,
throttle_classes
action, api_view, authentication_classes, parser_classes,
permission_classes, renderer_classes, schema, throttle_classes
)
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
@ -285,39 +284,3 @@ class ActionDecoratorTestCase(TestCase):
@test_action.mapping.post
def test_action():
raise NotImplementedError
def test_detail_route_deprecation(self):
with pytest.warns(RemovedInDRF310Warning) as record:
@detail_route()
def view(request):
raise NotImplementedError
assert len(record) == 1
assert str(record[0].message) == (
"`detail_route` is deprecated and will be removed in "
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
"`@action(detail=True)` instead."
)
def test_list_route_deprecation(self):
with pytest.warns(RemovedInDRF310Warning) as record:
@list_route()
def view(request):
raise NotImplementedError
assert len(record) == 1
assert str(record[0].message) == (
"`list_route` is deprecated and will be removed in "
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
"`@action(detail=False)` instead."
)
def test_route_url_name_from_path(self):
# pre-3.8 behavior was to base the `url_name` off of the `url_path`
with pytest.warns(RemovedInDRF310Warning):
@list_route(url_path='foo_bar')
def view(request):
raise NotImplementedError
assert view.url_path == 'foo_bar'
assert view.url_name == 'foo-bar'

View File

@ -1,21 +1,20 @@
import base64
import unittest
import warnings
from unittest import mock
import django
import pytest
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, Group, Permission, User
from django.db import models
from django.test import TestCase
from django.urls import ResolverMatch
from rest_framework import (
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
permissions, serializers, status, views
HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers,
status, views
)
from rest_framework.compat import PY36, is_guardian_installed
from rest_framework.filters import DjangoObjectPermissionsFilter
from rest_framework.compat import PY36
from rest_framework.routers import DefaultRouter
from rest_framework.test import APIRequestFactory
from tests.models import BasicModel
@ -309,7 +308,7 @@ class GetQuerysetObjectPermissionInstanceView(generics.RetrieveUpdateDestroyAPIV
get_queryset_object_permissions_view = GetQuerysetObjectPermissionInstanceView.as_view()
@unittest.skipUnless(is_guardian_installed(), 'django-guardian not installed')
@unittest.skipUnless('guardian' in settings.INSTALLED_APPS, 'django-guardian not installed')
class ObjectPermissionsIntegrationTests(TestCase):
"""
Integration tests for the object level permissions API.
@ -418,37 +417,14 @@ class ObjectPermissionsIntegrationTests(TestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Read list
def test_django_object_permissions_filter_deprecated(self):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("always")
DjangoObjectPermissionsFilter()
message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved "
"to the 3rd-party django-rest-framework-guardian package.")
self.assertEqual(len(w), 1)
self.assertIs(w[-1].category, RemovedInDRF310Warning)
self.assertEqual(str(w[-1].message), message)
# Note: this previously tested `DjangoObjectPermissionsFilter`, which has
# since been moved to a separate package. These now act as sanity checks.
def test_can_read_list_permissions(self):
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['readonly'])
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
# TODO: remove in version 3.10
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
response = object_permissions_list_view(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data[0].get('id'), 1)
def test_cannot_read_list_permissions(self):
request = factory.get('/', HTTP_AUTHORIZATION=self.credentials['writeonly'])
object_permissions_list_view.cls.filter_backends = (DjangoObjectPermissionsFilter,)
# TODO: remove in version 3.10
with warnings.catch_warnings(record=True):
warnings.simplefilter("always")
response = object_permissions_list_view(request)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(response.data, [])
def test_cannot_method_not_allowed(self):
request = factory.generic('METHOD_NOT_ALLOWED', '/', HTTP_AUTHORIZATION=self.credentials['readonly'])
response = object_permissions_list_view(request)