Skip to content

Commit 994e1ba

Browse files
committed
Django 1.10 support. (#4158)
* Added TEMPLATES setting to tests * Remove deprecated view-string in URL conf * Replace 'urls = ...' in test classes with override_settings('ROOT_URLCONF=...') * Refactor UsingURLPatterns to use override_settings(ROOT_URLCONF=...) style * Get model managers and names in a version-compatible manner. * Apply override_settings to a TestCase, not a mixin class * Use '.callback' property instead of private attributes when inspecting urlpatterns * Pass 'user' to template explicitly * Correct sorting of import statements. * Remove unused TEMPLATE_LOADERS setting, in favor of TEMPLATES. * Remove code style issue * BaseFilter test requires a concrete model * Resolve tox.ini issues * Resolve isort differences between local and tox environments
1 parent fe2aede commit 994e1ba

24 files changed

+114
-123
lines changed

Diff for: rest_framework/compat.py

+17
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ def distinct(queryset, base):
5858
return queryset.distinct()
5959

6060

61+
def get_names_and_managers(options):
62+
if django.VERSION >= (1, 10):
63+
# Django 1.10 onwards provides a `.managers` property on the Options.
64+
return [
65+
(manager.name, manager)
66+
for manager
67+
in options.managers
68+
]
69+
# For Django 1.8 and 1.9, use the three-tuple information provided
70+
# by .concrete_managers and .abstract_managers
71+
return [
72+
(manager_info[1], manager_info[2])
73+
for manager_info
74+
in (options.concrete_managers + options.abstract_managers)
75+
]
76+
77+
6178
# contrib.postgres only supported from 1.8 onwards.
6279
try:
6380
from django.contrib.postgres import fields as postgres_fields

Diff for: rest_framework/renderers.py

+1
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ def get_context(self, data, accepted_media_type, renderer_context):
635635
'view': view,
636636
'request': request,
637637
'response': response,
638+
'user': request.user,
638639
'description': self.get_description(view, response.status_code),
639640
'name': self.get_name(view),
640641
'version': VERSION,

Diff for: rest_framework/urlpatterns.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required):
2424
else:
2525
# Regular URL pattern
2626
regex = urlpattern.regex.pattern.rstrip('$').rstrip('/') + suffix_pattern
27-
view = urlpattern._callback or urlpattern._callback_str
27+
view = urlpattern.callback
2828
kwargs = urlpattern.default_args
2929
name = urlpattern.name
3030
# Add in both the existing and the new urlpattern

Diff for: rest_framework/utils/representation.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
from django.utils.encoding import force_text
1111
from django.utils.functional import Promise
1212

13-
from rest_framework.compat import unicode_repr
13+
from rest_framework.compat import get_names_and_managers, unicode_repr
1414

1515

1616
def manager_repr(value):
1717
model = value.model
1818
opts = model._meta
19-
for _, name, manager in opts.concrete_managers + opts.abstract_managers:
20-
if manager == value:
21-
return '%s.%s.all()' % (model._meta.object_name, name)
19+
for manager_name, manager_instance in get_names_and_managers(opts):
20+
if manager_instance == value:
21+
return '%s.%s.all()' % (model._meta.object_name, manager_name)
2222
return repr(value)
2323

2424

Diff for: runtests.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
FLAKE8_ARGS = ['rest_framework', 'tests', '--ignore=E501']
1616

17-
ISORT_ARGS = ['--recursive', '--check-only', 'rest_framework', 'tests']
17+
ISORT_ARGS = ['--recursive', '--check-only', '-p', 'tests', 'rest_framework', 'tests']
1818

1919
sys.path.append(os.path.dirname(__file__))
2020

Diff for: tests/browsable_api/test_browsable_api.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
from __future__ import unicode_literals
22

33
from django.contrib.auth.models import User
4-
from django.test import TestCase
4+
from django.test import TestCase, override_settings
55

66
from rest_framework.test import APIClient
77

88

9+
@override_settings(ROOT_URLCONF='tests.browsable_api.auth_urls')
910
class DropdownWithAuthTests(TestCase):
1011
"""Tests correct dropdown behaviour with Auth views enabled."""
11-
12-
urls = 'tests.browsable_api.auth_urls'
13-
1412
def setUp(self):
1513
self.client = APIClient(enforce_csrf_checks=True)
1614
self.username = 'john'
@@ -40,11 +38,9 @@ def test_login_shown_when_logged_out(self):
4038
self.assertContains(response, '>Log in<')
4139

4240

41+
@override_settings(ROOT_URLCONF='tests.browsable_api.no_auth_urls')
4342
class NoDropdownWithoutAuthTests(TestCase):
4443
"""Tests correct dropdown behaviour with Auth views NOT enabled."""
45-
46-
urls = 'tests.browsable_api.no_auth_urls'
47-
4844
def setUp(self):
4945
self.client = APIClient(enforce_csrf_checks=True)
5046
self.username = 'john'

Diff for: tests/browsable_api/views.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66

77

88
class MockView(APIView):
9-
109
authentication_classes = (authentication.SessionAuthentication,)
11-
renderer_classes = (renderers.BrowsableAPIRenderer,)
10+
renderer_classes = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)
1211

1312
def get(self, request):
1413
return Response({'a': 1, 'b': 2, 'c': 3})

Diff for: tests/conftest.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ def pytest_configure():
33

44
settings.configure(
55
DEBUG_PROPAGATE_EXCEPTIONS=True,
6-
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3',
7-
'NAME': ':memory:'}},
6+
DATABASES={
7+
'default': {
8+
'ENGINE': 'django.db.backends.sqlite3',
9+
'NAME': ':memory:'
10+
}
11+
},
812
SITE_ID=1,
913
SECRET_KEY='not very secret in tests',
1014
USE_I18N=True,
1115
USE_L10N=True,
1216
STATIC_URL='/static/',
1317
ROOT_URLCONF='tests.urls',
14-
TEMPLATE_LOADERS=(
15-
'django.template.loaders.filesystem.Loader',
16-
'django.template.loaders.app_directories.Loader',
17-
),
18+
TEMPLATES=[
19+
{
20+
'BACKEND': 'django.template.backends.django.DjangoTemplates',
21+
'APP_DIRS': True,
22+
},
23+
],
1824
MIDDLEWARE_CLASSES=(
1925
'django.middleware.common.CommonMiddleware',
2026
'django.contrib.sessions.middleware.SessionMiddleware',
@@ -27,7 +33,6 @@ def pytest_configure():
2733
'django.contrib.sessions',
2834
'django.contrib.sites',
2935
'django.contrib.staticfiles',
30-
3136
'rest_framework',
3237
'rest_framework.authtoken',
3338
'tests',

Diff for: tests/models.py

-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ class BasicModel(RESTFrameworkModel):
2727
class BaseFilterableItem(RESTFrameworkModel):
2828
text = models.CharField(max_length=100)
2929

30-
class Meta:
31-
abstract = True
32-
3330

3431
class FilterableItem(BaseFilterableItem):
3532
decimal = models.DecimalField(max_digits=4, decimal_places=2)

Diff for: tests/test_authentication.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.contrib.auth.models import User
99
from django.db import models
1010
from django.http import HttpResponse
11-
from django.test import TestCase
11+
from django.test import TestCase, override_settings
1212
from django.utils import six
1313

1414
from rest_framework import (
@@ -19,6 +19,7 @@
1919
TokenAuthentication
2020
)
2121
from rest_framework.authtoken.models import Token
22+
from rest_framework.authtoken.views import obtain_auth_token
2223
from rest_framework.response import Response
2324
from rest_framework.test import APIClient, APIRequestFactory
2425
from rest_framework.views import APIView
@@ -75,15 +76,14 @@ def put(self, request):
7576
authentication_classes=[CustomKeywordTokenAuthentication]
7677
)
7778
),
78-
url(r'^auth-token/$', 'rest_framework.authtoken.views.obtain_auth_token'),
79+
url(r'^auth-token/$', obtain_auth_token),
7980
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
8081
]
8182

8283

84+
@override_settings(ROOT_URLCONF='tests.test_authentication')
8385
class BasicAuthTests(TestCase):
8486
"""Basic authentication"""
85-
urls = 'tests.test_authentication'
86-
8787
def setUp(self):
8888
self.csrf_client = APIClient(enforce_csrf_checks=True)
8989
self.username = 'john'
@@ -151,10 +151,9 @@ def test_post_json_failing_basic_auth(self):
151151
self.assertEqual(response['WWW-Authenticate'], 'Basic realm="api"')
152152

153153

154+
@override_settings(ROOT_URLCONF='tests.test_authentication')
154155
class SessionAuthTests(TestCase):
155156
"""User session authentication"""
156-
urls = 'tests.test_authentication'
157-
158157
def setUp(self):
159158
self.csrf_client = APIClient(enforce_csrf_checks=True)
160159
self.non_csrf_client = APIClient(enforce_csrf_checks=False)
@@ -223,7 +222,6 @@ def test_post_form_session_auth_failing(self):
223222

224223
class BaseTokenAuthTests(object):
225224
"""Token authentication"""
226-
urls = 'tests.test_authentication'
227225
model = None
228226
path = None
229227
header_prefix = 'Token '
@@ -311,6 +309,7 @@ def test_post_json_failing_token_auth(self):
311309
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
312310

313311

312+
@override_settings(ROOT_URLCONF='tests.test_authentication')
314313
class TokenAuthTests(BaseTokenAuthTests, TestCase):
315314
model = Token
316315
path = '/token/'
@@ -367,11 +366,13 @@ def test_token_login_form(self):
367366
self.assertEqual(response.data['token'], self.key)
368367

369368

369+
@override_settings(ROOT_URLCONF='tests.test_authentication')
370370
class CustomTokenAuthTests(BaseTokenAuthTests, TestCase):
371371
model = CustomToken
372372
path = '/customtoken/'
373373

374374

375+
@override_settings(ROOT_URLCONF='tests.test_authentication')
375376
class CustomKeywordTokenAuthTests(BaseTokenAuthTests, TestCase):
376377
model = Token
377378
path = '/customkeywordtoken/'

Diff for: tests/test_filters.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -274,12 +274,11 @@ def test_unknown_filter(self):
274274
self.assertEqual(response.status_code, status.HTTP_200_OK)
275275

276276

277+
@override_settings(ROOT_URLCONF='tests.test_filters')
277278
class IntegrationTestDetailFiltering(CommonFilteringTestCase):
278279
"""
279280
Integration tests for filtered detail views.
280281
"""
281-
urls = 'tests.test_filters'
282-
283282
def _get_url(self, item):
284283
return reverse('detail-view', kwargs=dict(pk=item.pk))
285284

Diff for: tests/test_htmlrenderer.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.core.exceptions import PermissionDenied
66
from django.http import Http404
77
from django.template import Template, TemplateDoesNotExist
8-
from django.test import TestCase
8+
from django.test import TestCase, override_settings
99
from django.utils import six
1010

1111
from rest_framework import status
@@ -43,9 +43,8 @@ def not_found(request):
4343
]
4444

4545

46+
@override_settings(ROOT_URLCONF='tests.test_htmlrenderer')
4647
class TemplateHTMLRendererTests(TestCase):
47-
urls = 'tests.test_htmlrenderer'
48-
4948
def setUp(self):
5049
"""
5150
Monkeypatch get_template
@@ -89,9 +88,8 @@ def test_permission_denied_html_view(self):
8988
self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8')
9089

9190

91+
@override_settings(ROOT_URLCONF='tests.test_htmlrenderer')
9292
class TemplateHTMLRendererExceptionTests(TestCase):
93-
urls = 'tests.test_htmlrenderer'
94-
9593
def setUp(self):
9694
"""
9795
Monkeypatch get_template

Diff for: tests/test_middleware.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
21
from django.conf.urls import url
32
from django.contrib.auth.models import User
3+
from django.test import override_settings
44

55
from rest_framework.authentication import TokenAuthentication
66
from rest_framework.authtoken.models import Token
@@ -20,10 +20,8 @@ def process_response(self, request, response):
2020
return response
2121

2222

23+
@override_settings(ROOT_URLCONF='tests.test_middleware')
2324
class TestMiddleware(APITestCase):
24-
25-
urls = 'tests.test_middleware'
26-
2725
def test_middleware_can_access_user_when_processing_response(self):
2826
user = User.objects.create_user('john', 'john@example.com', 'password')
2927
key = 'abcd1234'

Diff for: tests/test_relations_hyperlink.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import unicode_literals
22

33
from django.conf.urls import url
4-
from django.test import TestCase
4+
from django.test import TestCase, override_settings
55

66
from rest_framework import serializers
77
from rest_framework.test import APIRequestFactory
@@ -71,10 +71,8 @@ class Meta:
7171

7272

7373
# TODO: Add test that .data cannot be accessed prior to .is_valid
74-
74+
@override_settings(ROOT_URLCONF='tests.test_relations_hyperlink')
7575
class HyperlinkedManyToManyTests(TestCase):
76-
urls = 'tests.test_relations_hyperlink'
77-
7876
def setUp(self):
7977
for idx in range(1, 4):
8078
target = ManyToManyTarget(name='target-%d' % idx)
@@ -188,9 +186,8 @@ def test_reverse_many_to_many_create(self):
188186
self.assertEqual(serializer.data, expected)
189187

190188

189+
@override_settings(ROOT_URLCONF='tests.test_relations_hyperlink')
191190
class HyperlinkedForeignKeyTests(TestCase):
192-
urls = 'tests.test_relations_hyperlink'
193-
194191
def setUp(self):
195192
target = ForeignKeyTarget(name='target-1')
196193
target.save()
@@ -318,9 +315,8 @@ def test_foreign_key_update_with_invalid_null(self):
318315
self.assertEqual(serializer.errors, {'target': ['This field may not be null.']})
319316

320317

318+
@override_settings(ROOT_URLCONF='tests.test_relations_hyperlink')
321319
class HyperlinkedNullableForeignKeyTests(TestCase):
322-
urls = 'tests.test_relations_hyperlink'
323-
324320
def setUp(self):
325321
target = ForeignKeyTarget(name='target-1')
326322
target.save()
@@ -425,9 +421,8 @@ def test_foreign_key_update_with_valid_emptystring(self):
425421
self.assertEqual(serializer.data, expected)
426422

427423

424+
@override_settings(ROOT_URLCONF='tests.test_relations_hyperlink')
428425
class HyperlinkedNullableOneToOneTests(TestCase):
429-
urls = 'tests.test_relations_hyperlink'
430-
431426
def setUp(self):
432427
target = OneToOneTarget(name='target-1')
433428
target.save()

Diff for: tests/test_renderers.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from django.conf.urls import include, url
99
from django.core.cache import cache
1010
from django.db import models
11-
from django.test import TestCase
11+
from django.test import TestCase, override_settings
1212
from django.utils import six
1313
from django.utils.safestring import SafeText
1414
from django.utils.translation import ugettext_lazy as _
@@ -148,13 +148,11 @@ def test_only_permitted_forms_are_displayed(self):
148148
self.assertContains(response, '>PATCH<')
149149

150150

151+
@override_settings(ROOT_URLCONF='tests.test_renderers')
151152
class RendererEndToEndTests(TestCase):
152153
"""
153154
End-to-end testing of renderers using an RendererMixin on a generic view.
154155
"""
155-
156-
urls = 'tests.test_renderers'
157-
158156
def test_default_renderer_serializes_content(self):
159157
"""If the Accept header is not set the default renderer should serialize the response."""
160158
resp = self.client.get('/')
@@ -397,13 +395,11 @@ class AsciiJSONRenderer(JSONRenderer):
397395

398396

399397
# Tests for caching issue, #346
398+
@override_settings(ROOT_URLCONF='tests.test_renderers')
400399
class CacheRenderTest(TestCase):
401400
"""
402401
Tests specific to caching responses
403402
"""
404-
405-
urls = 'tests.test_renderers'
406-
407403
def test_head_caching(self):
408404
"""
409405
Test caching of HEAD requests

0 commit comments

Comments
 (0)