mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-29 13:04:03 +03:00
Introduce RemovedInDRF…Warning classes to simplify deprecations. (#6480)
Closes #6290.
This commit is contained in:
parent
a216d02ce0
commit
94593b3a50
|
@ -18,9 +18,9 @@ REST framework releases follow a formal deprecation policy, which is in line wit
|
||||||
|
|
||||||
The timeline for deprecation of a feature present in version 1.0 would work as follows:
|
The timeline for deprecation of a feature present in version 1.0 would work as follows:
|
||||||
|
|
||||||
* Version 1.1 would remain **fully backwards compatible** with 1.0, but would raise `PendingDeprecationWarning` warnings if you use the feature that are due to be deprecated. These warnings are **silent by default**, but can be explicitly enabled when you're ready to start migrating any required changes. For example if you start running your tests using `python -Wd manage.py test`, you'll be warned of any API changes you need to make.
|
* Version 1.1 would remain **fully backwards compatible** with 1.0, but would raise `RemovedInDRF13Warning` warnings, subclassing `PendingDeprecationWarning`, if you use the feature that are due to be deprecated. These warnings are **silent by default**, but can be explicitly enabled when you're ready to start migrating any required changes. For example if you start running your tests using `python -Wd manage.py test`, you'll be warned of any API changes you need to make.
|
||||||
|
|
||||||
* Version 1.2 would escalate these warnings to `DeprecationWarning`, which is loud by default.
|
* Version 1.2 would escalate these warnings to subclass `DeprecationWarning`, which is loud by default.
|
||||||
|
|
||||||
* Version 1.3 would remove the deprecated bits of API entirely.
|
* Version 1.3 would remove the deprecated bits of API entirely.
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,11 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
|
||||||
ISO_8601 = 'iso-8601'
|
ISO_8601 = 'iso-8601'
|
||||||
|
|
||||||
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
|
default_app_config = 'rest_framework.apps.RestFrameworkConfig'
|
||||||
|
|
||||||
|
|
||||||
|
class RemovedInDRF310Warning(DeprecationWarning):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RemovedInDRF311Warning(PendingDeprecationWarning):
|
||||||
|
pass
|
||||||
|
|
|
@ -14,6 +14,7 @@ import warnings
|
||||||
from django.forms.utils import pretty_name
|
from django.forms.utils import pretty_name
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
from rest_framework import RemovedInDRF310Warning
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,7 +226,7 @@ def detail_route(methods=None, **kwargs):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"`detail_route` is deprecated and will be removed in 3.10 in favor of "
|
"`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.",
|
"`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.",
|
||||||
DeprecationWarning, stacklevel=2
|
RemovedInDRF310Warning, stacklevel=2
|
||||||
)
|
)
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
@ -243,7 +244,7 @@ def list_route(methods=None, **kwargs):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"`list_route` is deprecated and will be removed in 3.10 in favor of "
|
"`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.",
|
"`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.",
|
||||||
DeprecationWarning, stacklevel=2
|
RemovedInDRF310Warning, stacklevel=2
|
||||||
)
|
)
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
|
|
|
@ -17,6 +17,7 @@ from django.utils import six
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from rest_framework import RemovedInDRF310Warning
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
coreapi, coreschema, distinct, is_guardian_installed
|
coreapi, coreschema, distinct, is_guardian_installed
|
||||||
)
|
)
|
||||||
|
@ -299,7 +300,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"`DjangoObjectPermissionsFilter` has been deprecated and moved to "
|
"`DjangoObjectPermissionsFilter` has been deprecated and moved to "
|
||||||
"the 3rd-party django-rest-framework-guardian package.",
|
"the 3rd-party django-rest-framework-guardian package.",
|
||||||
DeprecationWarning, stacklevel=2
|
RemovedInDRF310Warning, stacklevel=2
|
||||||
)
|
)
|
||||||
assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'
|
assert is_guardian_installed(), 'Using DjangoObjectPermissionsFilter, but django-guardian is not installed'
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,9 @@ from django.urls import NoReverseMatch
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RenameMethodsBase
|
from django.utils.deprecation import RenameMethodsBase
|
||||||
|
|
||||||
from rest_framework import views
|
from rest_framework import (
|
||||||
|
RemovedInDRF310Warning, RemovedInDRF311Warning, views
|
||||||
|
)
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
from rest_framework.schemas import SchemaGenerator
|
from rest_framework.schemas import SchemaGenerator
|
||||||
|
@ -43,7 +45,7 @@ class DynamicDetailRoute(object):
|
||||||
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
|
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
|
||||||
"in favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
|
"in favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
|
||||||
"`DynamicRoute(url, name, True, initkwargs)` instead.",
|
"`DynamicRoute(url, name, True, initkwargs)` instead.",
|
||||||
DeprecationWarning, stacklevel=2
|
RemovedInDRF310Warning, stacklevel=2
|
||||||
)
|
)
|
||||||
return DynamicRoute(url, name, True, initkwargs)
|
return DynamicRoute(url, name, True, initkwargs)
|
||||||
|
|
||||||
|
@ -54,7 +56,7 @@ class DynamicListRoute(object):
|
||||||
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
|
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
|
||||||
"favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
|
"favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
|
||||||
"`DynamicRoute(url, name, False, initkwargs)` instead.",
|
"`DynamicRoute(url, name, False, initkwargs)` instead.",
|
||||||
DeprecationWarning, stacklevel=2
|
RemovedInDRF310Warning, stacklevel=2
|
||||||
)
|
)
|
||||||
return DynamicRoute(url, name, False, initkwargs)
|
return DynamicRoute(url, name, False, initkwargs)
|
||||||
|
|
||||||
|
@ -77,7 +79,7 @@ def flatten(list_of_lists):
|
||||||
|
|
||||||
class RenameRouterMethods(RenameMethodsBase):
|
class RenameRouterMethods(RenameMethodsBase):
|
||||||
renamed_methods = (
|
renamed_methods = (
|
||||||
('get_default_base_name', 'get_default_basename', PendingDeprecationWarning),
|
('get_default_base_name', 'get_default_basename', RemovedInDRF311Warning),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ class BaseRouter(six.with_metaclass(RenameRouterMethods)):
|
||||||
def register(self, prefix, viewset, basename=None, base_name=None):
|
def register(self, prefix, viewset, basename=None, base_name=None):
|
||||||
if base_name is not None:
|
if base_name is not None:
|
||||||
msg = "The `base_name` argument is pending deprecation in favor of `basename`."
|
msg = "The `base_name` argument is pending deprecation in favor of `basename`."
|
||||||
warnings.warn(msg, PendingDeprecationWarning, 2)
|
warnings.warn(msg, RemovedInDRF311Warning, 2)
|
||||||
|
|
||||||
assert not (basename and base_name), (
|
assert not (basename and base_name), (
|
||||||
"Do not provide both the `basename` and `base_name` arguments.")
|
"Do not provide both the `basename` and `base_name` arguments.")
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||||
import pytest
|
import pytest
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import RemovedInDRF310Warning, status
|
||||||
from rest_framework.authentication import BasicAuthentication
|
from rest_framework.authentication import BasicAuthentication
|
||||||
from rest_framework.decorators import (
|
from rest_framework.decorators import (
|
||||||
action, api_view, authentication_classes, detail_route, list_route,
|
action, api_view, authentication_classes, detail_route, list_route,
|
||||||
|
@ -290,7 +290,7 @@ class ActionDecoratorTestCase(TestCase):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def test_detail_route_deprecation(self):
|
def test_detail_route_deprecation(self):
|
||||||
with pytest.warns(DeprecationWarning) as record:
|
with pytest.warns(RemovedInDRF310Warning) as record:
|
||||||
@detail_route()
|
@detail_route()
|
||||||
def view(request):
|
def view(request):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -303,7 +303,7 @@ class ActionDecoratorTestCase(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_list_route_deprecation(self):
|
def test_list_route_deprecation(self):
|
||||||
with pytest.warns(DeprecationWarning) as record:
|
with pytest.warns(RemovedInDRF310Warning) as record:
|
||||||
@list_route()
|
@list_route()
|
||||||
def view(request):
|
def view(request):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -317,7 +317,7 @@ class ActionDecoratorTestCase(TestCase):
|
||||||
|
|
||||||
def test_route_url_name_from_path(self):
|
def test_route_url_name_from_path(self):
|
||||||
# pre-3.8 behavior was to base the `url_name` off of the `url_path`
|
# pre-3.8 behavior was to base the `url_name` off of the `url_path`
|
||||||
with pytest.warns(DeprecationWarning):
|
with pytest.warns(RemovedInDRF310Warning):
|
||||||
@list_route(url_path='foo_bar')
|
@list_route(url_path='foo_bar')
|
||||||
def view(request):
|
def view(request):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -12,8 +12,8 @@ from django.test import TestCase
|
||||||
from django.urls import ResolverMatch
|
from django.urls import ResolverMatch
|
||||||
|
|
||||||
from rest_framework import (
|
from rest_framework import (
|
||||||
HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers,
|
HTTP_HEADER_ENCODING, RemovedInDRF310Warning, authentication, generics,
|
||||||
status, views
|
permissions, serializers, status, views
|
||||||
)
|
)
|
||||||
from rest_framework.compat import PY36, is_guardian_installed, mock
|
from rest_framework.compat import PY36, is_guardian_installed, mock
|
||||||
from rest_framework.filters import DjangoObjectPermissionsFilter
|
from rest_framework.filters import DjangoObjectPermissionsFilter
|
||||||
|
@ -427,7 +427,7 @@ class ObjectPermissionsIntegrationTests(TestCase):
|
||||||
message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved "
|
message = ("`DjangoObjectPermissionsFilter` has been deprecated and moved "
|
||||||
"to the 3rd-party django-rest-framework-guardian package.")
|
"to the 3rd-party django-rest-framework-guardian package.")
|
||||||
self.assertEqual(len(w), 1)
|
self.assertEqual(len(w), 1)
|
||||||
self.assertIs(w[-1].category, DeprecationWarning)
|
self.assertIs(w[-1].category, RemovedInDRF310Warning)
|
||||||
self.assertEqual(str(w[-1].message), message)
|
self.assertEqual(str(w[-1].message), message)
|
||||||
|
|
||||||
def test_can_read_list_permissions(self):
|
def test_can_read_list_permissions(self):
|
||||||
|
|
|
@ -10,7 +10,9 @@ from django.db import models
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.urls import resolve, reverse
|
from django.urls import resolve, reverse
|
||||||
|
|
||||||
from rest_framework import permissions, serializers, viewsets
|
from rest_framework import (
|
||||||
|
RemovedInDRF311Warning, permissions, serializers, viewsets
|
||||||
|
)
|
||||||
from rest_framework.compat import get_regex_pattern
|
from rest_framework.compat import get_regex_pattern
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
@ -508,7 +510,7 @@ class TestBaseNameRename(TestCase):
|
||||||
def test_base_name_argument_deprecation(self):
|
def test_base_name_argument_deprecation(self):
|
||||||
router = SimpleRouter()
|
router = SimpleRouter()
|
||||||
|
|
||||||
with pytest.warns(PendingDeprecationWarning) as w:
|
with pytest.warns(RemovedInDRF311Warning) as w:
|
||||||
warnings.simplefilter('always')
|
warnings.simplefilter('always')
|
||||||
router.register('mock', MockViewSet, base_name='mock')
|
router.register('mock', MockViewSet, base_name='mock')
|
||||||
|
|
||||||
|
@ -535,7 +537,7 @@ class TestBaseNameRename(TestCase):
|
||||||
msg = "`CustomRouter.get_default_base_name` method should be renamed `get_default_basename`."
|
msg = "`CustomRouter.get_default_base_name` method should be renamed `get_default_basename`."
|
||||||
|
|
||||||
# Class definition should raise a warning
|
# Class definition should raise a warning
|
||||||
with pytest.warns(PendingDeprecationWarning) as w:
|
with pytest.warns(RemovedInDRF311Warning) as w:
|
||||||
warnings.simplefilter('always')
|
warnings.simplefilter('always')
|
||||||
|
|
||||||
class CustomRouter(SimpleRouter):
|
class CustomRouter(SimpleRouter):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user