diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27bbcb763..a9c053fcb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,3 +37,13 @@ repos: hooks: - id: pyupgrade args: ["--py39-plus", "--keep-percent-format"] + +- repo: https://github.com/adamchainz/django-upgrade + rev: 1.24.0 + hooks: + - id: django-upgrade + args: [ + --target-version, "4.2", + # test_urlpatterns.py has re_path related tests + --skip, "django_urls", "tests/test_urlpatterns.py", + ] diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 3f3bd2227..58ef9d2e1 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -17,7 +17,7 @@ def get_authorization_header(request): Hide some test client ickyness where the header can be unicode. """ - auth = request.META.get('HTTP_AUTHORIZATION', b'') + auth = request.headers.get('authorization', b'') if isinstance(auth, str): # Work around django test client oddness auth = auth.encode(HTTP_HEADER_ENCODING) diff --git a/rest_framework/authtoken/admin.py b/rest_framework/authtoken/admin.py index eabb8fca8..0c98c6a0d 100644 --- a/rest_framework/authtoken/admin.py +++ b/rest_framework/authtoken/admin.py @@ -21,6 +21,7 @@ class TokenChangeList(ChangeList): current_app=self.model_admin.admin_site.name) +@admin.register(TokenProxy) class TokenAdmin(admin.ModelAdmin): list_display = ('key', 'user', 'created') fields = ('user',) @@ -49,6 +50,3 @@ class TokenAdmin(admin.ModelAdmin): # Map back to actual Token, since delete() uses pk. token = Token.objects.get(key=obj.key) return super().delete_model(request, token) - - -admin.site.register(TokenProxy, TokenAdmin) diff --git a/rest_framework/negotiation.py b/rest_framework/negotiation.py index 23012f71f..d0b43ccd8 100644 --- a/rest_framework/negotiation.py +++ b/rest_framework/negotiation.py @@ -93,5 +93,5 @@ class DefaultContentNegotiation(BaseContentNegotiation): Given the incoming request, return a tokenized list of media type strings. """ - header = request.META.get('HTTP_ACCEPT', '*/*') + header = request.headers.get('accept', '*/*') return [token.strip() for token in header.split(',')] diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index c0d6cf42f..90ce01c42 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -26,7 +26,7 @@ class BaseThrottle: if present and number of proxies is > 0. If not use all of HTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR. """ - xff = request.META.get('HTTP_X_FORWARDED_FOR') + xff = request.headers.get('x-forwarded-for') remote_addr = request.META.get('REMOTE_ADDR') num_proxies = api_settings.NUM_PROXIES diff --git a/tests/test_api_client.py b/tests/test_api_client.py index 976f10ed1..b2d322de2 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -4,7 +4,7 @@ import unittest from django.http import HttpResponse from django.test import override_settings -from django.urls import path, re_path +from django.urls import path from rest_framework.compat import coreapi, coreschema from rest_framework.parsers import FileUploadParser @@ -180,7 +180,7 @@ class HeadersView(APIView): urlpatterns = [ path('', SchemaView.as_view()), path('example/', ListView.as_view()), - re_path(r'^example/(?P[0-9]+)/$', DetailView.as_view()), + path('example//', DetailView.as_view()), path('upload/', UploadView.as_view()), path('download/', DownloadView.as_view()), path('text/', TextView.as_view()), diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 11d4bc01e..bcd076cde 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -102,7 +102,7 @@ class TestMiddleware(APITestCase): key = 'abcd1234' Token.objects.create(key=key, user=user) - self.client.get('/auth', HTTP_AUTHORIZATION='Token %s' % key) + self.client.get('/auth', headers={"authorization": 'Token %s' % key}) @override_settings(MIDDLEWARE=('tests.test_middleware.RequestPOSTMiddleware',)) def test_middleware_can_access_request_post_when_processing_response(self): diff --git a/tests/test_relations.py b/tests/test_relations.py index b9ab15789..28f8e9d66 100644 --- a/tests/test_relations.py +++ b/tests/test_relations.py @@ -4,7 +4,7 @@ import pytest from _pytest.monkeypatch import MonkeyPatch from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist from django.test import override_settings -from django.urls import re_path +from django.urls import path from django.utils.datastructures import MultiValueDict from rest_framework import relations, serializers @@ -152,7 +152,7 @@ class TestProxiedPrimaryKeyRelatedField(APISimpleTestCase): urlpatterns = [ - re_path(r'^example/(?P.+)/$', lambda: None, name='example'), + path('example//', lambda: None, name='example'), ] diff --git a/tests/test_renderers.py b/tests/test_renderers.py index 1b396575d..b5b28c17f 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -174,7 +174,7 @@ class RendererEndToEndTests(TestCase): def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" - resp = self.client.get('/', HTTP_ACCEPT='*/*') + resp = self.client.get('/', headers={"accept": '*/*'}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -182,7 +182,7 @@ class RendererEndToEndTests(TestCase): def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) + resp = self.client.get('/', headers={"accept": RendererA.media_type}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -190,14 +190,14 @@ class RendererEndToEndTests(TestCase): def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) + resp = self.client.get('/', headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) def test_unsatisfiable_accept_header_on_request_returns_406_status(self): """If the Accept header is unsatisfiable we should return a 406 Not Acceptable response.""" - resp = self.client.get('/', HTTP_ACCEPT='foo/bar') + resp = self.client.get('/', headers={"accept": 'foo/bar'}) self.assertEqual(resp.status_code, status.HTTP_406_NOT_ACCEPTABLE) def test_specified_renderer_serializes_content_on_format_query(self): @@ -228,14 +228,14 @@ class RendererEndToEndTests(TestCase): RendererB.format ) resp = self.client.get('/' + param, - HTTP_ACCEPT=RendererB.media_type) + headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) def test_parse_error_renderers_browsable_api(self): """Invalid data should still render the browsable API correctly.""" - resp = self.client.post('/parseerror', data='foobar', content_type='application/json', HTTP_ACCEPT='text/html') + resp = self.client.post('/parseerror', data='foobar', content_type='application/json', headers={"accept": 'text/html'}) self.assertEqual(resp['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) @@ -714,13 +714,13 @@ class BrowsableAPIRendererTests(URLPatternsTestCase): assert result is None def test_extra_actions_dropdown(self): - resp = self.client.get('/api/examples/', HTTP_ACCEPT='text/html') + resp = self.client.get('/api/examples/', headers={"accept": 'text/html'}) assert 'id="extra-actions-menu"' in resp.content.decode() assert '/api/examples/list_action/' in resp.content.decode() assert '>Extra list action<' in resp.content.decode() def test_extra_actions_dropdown_not_authed(self): - resp = self.client.get('/api/unauth-examples/', HTTP_ACCEPT='text/html') + resp = self.client.get('/api/unauth-examples/', headers={"accept": 'text/html'}) assert 'id="extra-actions-menu"' not in resp.content.decode() assert '/api/examples/list_action/' not in resp.content.decode() assert '>Extra list action<' not in resp.content.decode() diff --git a/tests/test_requests_client.py b/tests/test_requests_client.py index c8e7be6ee..9b4f4795f 100644 --- a/tests/test_requests_client.py +++ b/tests/test_requests_client.py @@ -28,7 +28,7 @@ class Root(APIView): } post = request.POST json = None - if request.META.get('CONTENT_TYPE') == 'application/json': + if request.headers.get('content-type') == 'application/json': json = request.data return Response({ diff --git a/tests/test_response.py b/tests/test_response.py index 83f8a6717..781478b7a 100644 --- a/tests/test_response.py +++ b/tests/test_response.py @@ -151,7 +151,7 @@ class RendererIntegrationTests(TestCase): def test_default_renderer_serializes_content_on_accept_any(self): """If the Accept header is set to */* the default renderer should serialize the response.""" - resp = self.client.get('/', HTTP_ACCEPT='*/*') + resp = self.client.get('/', headers={"accept": '*/*'}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -159,7 +159,7 @@ class RendererIntegrationTests(TestCase): def test_specified_renderer_serializes_content_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for the default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererA.media_type) + resp = self.client.get('/', headers={"accept": RendererA.media_type}) self.assertEqual(resp['Content-Type'], RendererA.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_A_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -167,7 +167,7 @@ class RendererIntegrationTests(TestCase): def test_specified_renderer_serializes_content_non_default_case(self): """If the Accept header is set the specified renderer should serialize the response. (In this case we check that works for a non-default renderer)""" - resp = self.client.get('/', HTTP_ACCEPT=RendererB.media_type) + resp = self.client.get('/', headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) @@ -192,7 +192,7 @@ class RendererIntegrationTests(TestCase): """If both a 'format' query and a matching Accept header specified, the renderer with the matching format attribute should serialize the response.""" resp = self.client.get('/?format=%s' % RendererB.format, - HTTP_ACCEPT=RendererB.media_type) + headers={"accept": RendererB.media_type}) self.assertEqual(resp['Content-Type'], RendererB.media_type + '; charset=utf-8') self.assertEqual(resp.content, RENDERER_B_SERIALIZER(DUMMYCONTENT)) self.assertEqual(resp.status_code, DUMMYSTATUS) diff --git a/tests/test_testing.py b/tests/test_testing.py index 26a6e8ffb..db886d20a 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -21,7 +21,7 @@ from rest_framework.test import ( @api_view(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']) def view(request): - data = {'auth': request.META.get('HTTP_AUTHORIZATION', b'')} + data = {'auth': request.headers.get('authorization', b'')} if request.user: data['user'] = request.user.username if request.auth: @@ -347,7 +347,7 @@ class TestAPIRequestFactory(TestCase): data=None, content_type='application/json', ) - assert request.META['CONTENT_TYPE'] == 'application/json' + assert request.headers['content-type'] == 'application/json' class TestUrlPatternTestCase(URLPatternsTestCase): diff --git a/tests/test_urlpatterns.py b/tests/test_urlpatterns.py index adcd0a742..3da548aee 100644 --- a/tests/test_urlpatterns.py +++ b/tests/test_urlpatterns.py @@ -93,7 +93,7 @@ class FormatSuffixTests(TestCase): def test_format_suffix_django2_args(self): urlpatterns = [ path('convtest/', dummy_view), - re_path(r'^retest/(?P[0-9]+)$', dummy_view), + path('retest/', dummy_view), ] test_paths = [ URLTestPath('/convtest/42', (), {'pk': 42}), diff --git a/tests/test_versioning.py b/tests/test_versioning.py index b21646184..2c3535c3a 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -152,7 +152,7 @@ class TestURLReversing(URLPatternsTestCase, APITestCase): path('v1/', include((included, 'v1'), namespace='v1')), path('another/', dummy_view, name='another'), re_path(r'^(?P[v1|v2]+)/another/$', dummy_view, name='another'), - re_path(r'^(?P.+)/unversioned/$', dummy_view, name='unversioned'), + path('/unversioned/', dummy_view, name='unversioned'), ]