From ef109e270de3e93374f7ef1f0b23f39df4e47e85 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 16 Nov 2017 05:33:00 -0800 Subject: [PATCH] Remove references to unsupported Django versions in docs and code Per the trove classifiers, DRF only supports Django versions 1.10+. Can drop documentation, code comments, and workarounds for older Django versions. --- docs/api-guide/fields.md | 4 +--- docs/index.md | 4 ++-- docs/tutorial/1-serialization.md | 2 -- docs/tutorial/4-authentication-and-permissions.md | 5 ++--- rest_framework/authtoken/serializers.py | 12 ++++-------- rest_framework/renderers.py | 2 +- rest_framework/urls.py | 5 ++--- rest_framework/utils/model_meta.py | 10 ++-------- tests/test_atomic_requests.py | 5 ++--- tests/test_fields.py | 6 ------ tests/test_filters.py | 3 --- 11 files changed, 16 insertions(+), 42 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 64014b56e..d209a945b 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -356,8 +356,6 @@ Corresponds to `django.db.models.fields.DurationField` The `validated_data` for these fields will contain a `datetime.timedelta` instance. The representation is a string following this format `'[DD] [HH:[MM:]]ss[.uuuuuu]'`. -**Note:** This field is only available with Django versions >= 1.8. - **Signature:** `DurationField()` --- @@ -681,4 +679,4 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide [django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis [django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore [django-hstore]: https://github.com/djangonauts/django-hstore -[python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes \ No newline at end of file +[python-decimal-rounding-modes]: https://docs.python.org/3/library/decimal.html#rounding-modes diff --git a/docs/index.md b/docs/index.md index a902ed3af..0e747463b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -120,10 +120,10 @@ If you're intending to use the browsable API you'll probably also want to add RE urlpatterns = [ ... - url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) + url(r'^api-auth/', include('rest_framework.urls')) ] -Note that the URL path can be whatever you want, but you must include `'rest_framework.urls'` with the `'rest_framework'` namespace. You may leave out the namespace in Django 1.9+, and REST framework will set it for you. +Note that the URL path can be whatever you want. ## Example diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md index 558797816..a834c8dbb 100644 --- a/docs/tutorial/1-serialization.md +++ b/docs/tutorial/1-serialization.md @@ -48,8 +48,6 @@ We'll need to add our new `snippets` app and the `rest_framework` app to `INSTAL 'snippets.apps.SnippetsConfig', ) -Please note that if you're using Django <1.9, you need to replace `snippets.apps.SnippetsConfig` with `snippets`. - Okay, we're ready to roll. ## Creating a model to work with diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md index b43fabfac..72cf64e37 100644 --- a/docs/tutorial/4-authentication-and-permissions.md +++ b/docs/tutorial/4-authentication-and-permissions.md @@ -142,11 +142,10 @@ Add the following import at the top of the file: And, at the end of the file, add a pattern to include the login and logout views for the browsable API. urlpatterns += [ - url(r'^api-auth/', include('rest_framework.urls', - namespace='rest_framework')), + url(r'^api-auth/', include('rest_framework.urls'), ] -The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. The only restriction is that the included urls must use the `'rest_framework'` namespace. In Django 1.9+, REST framework will set the namespace, so you may leave it out. +The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again. diff --git a/rest_framework/authtoken/serializers.py b/rest_framework/authtoken/serializers.py index 301b6a0cb..01d2d40b9 100644 --- a/rest_framework/authtoken/serializers.py +++ b/rest_framework/authtoken/serializers.py @@ -20,14 +20,10 @@ class AuthTokenSerializer(serializers.Serializer): user = authenticate(request=self.context.get('request'), username=username, password=password) - if user: - # From Django 1.10 onwards the `authenticate` call simply - # returns `None` for is_active=False users. - # (Assuming the default `ModelBackend` authentication backend.) - if not user.is_active: - msg = _('User account is disabled.') - raise serializers.ValidationError(msg, code='authorization') - else: + # The authenticate call simply returns None for is_active=False + # users. (Assuming the default ModelBackend authentication + # backend.) + if not user: msg = _('Unable to log in with provided credentials.') raise serializers.ValidationError(msg, code='authorization') else: diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 3298294ce..bbefb4624 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -666,7 +666,7 @@ class BrowsableAPIRenderer(BaseRenderer): paginator = None csrf_cookie_name = settings.CSRF_COOKIE_NAME - csrf_header_name = getattr(settings, 'CSRF_HEADER_NAME', 'HTTP_X_CSRFToken') # Fallback for Django 1.8 + csrf_header_name = settings.CSRF_HEADER_NAME if csrf_header_name.startswith('HTTP_'): csrf_header_name = csrf_header_name[5:] csrf_header_name = csrf_header_name.replace('_', '-') diff --git a/rest_framework/urls.py b/rest_framework/urls.py index 10cc5def0..80fce5dc4 100644 --- a/rest_framework/urls.py +++ b/rest_framework/urls.py @@ -6,11 +6,10 @@ your API requires authentication: urlpatterns = [ ... - url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')) + url(r'^auth/', include('rest_framework.urls')) ] -In Django versions older than 1.9, the urls must be namespaced as 'rest_framework', -and you should make sure your authentication settings include `SessionAuthentication`. +You should make sure your authentication settings include `SessionAuthentication`. """ from __future__ import unicode_literals diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index f0ae02bb2..4cc93b8ef 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -105,18 +105,13 @@ def _get_reverse_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ - # Note that we have a hack here to handle internal API differences for - # this internal API across Django 1.7 -> Django 1.8. - # See: https://code.djangoproject.com/ticket/24208 - reverse_relations = OrderedDict() all_related_objects = [r for r in opts.related_objects if not r.field.many_to_many] for relation in all_related_objects: accessor_name = relation.get_accessor_name() - related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related_model=related, + related_model=relation.related_model, to_many=relation.field.remote_field.multiple, to_field=_get_to_field(relation.field), has_through_model=False, @@ -127,10 +122,9 @@ def _get_reverse_relationships(opts): all_related_many_to_many_objects = [r for r in opts.related_objects if r.field.many_to_many] for relation in all_related_many_to_many_objects: accessor_name = relation.get_accessor_name() - related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, - related_model=related, + related_model=relation.related_model, to_many=True, # manytomany do not have to_fields to_field=None, diff --git a/tests/test_atomic_requests.py b/tests/test_atomic_requests.py index f925ce3d3..697c549de 100644 --- a/tests/test_atomic_requests.py +++ b/tests/test_atomic_requests.py @@ -120,13 +120,12 @@ class DBTransactionAPIExceptionTests(TestCase): Transaction is rollbacked by our transaction atomic block. """ request = factory.post('/') - num_queries = (4 if getattr(connection.features, - 'can_release_savepoints', False) else 3) + num_queries = 4 if connection.features.can_release_savepoints else 3 with self.assertNumQueries(num_queries): # 1 - begin savepoint # 2 - insert # 3 - rollback savepoint - # 4 - release savepoint (django>=1.8 only) + # 4 - release savepoint with transaction.atomic(): response = self.view(request) assert transaction.get_rollback() diff --git a/tests/test_fields.py b/tests/test_fields.py index 101d3b26d..fc9ce192a 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -5,7 +5,6 @@ import unittest import uuid from decimal import ROUND_DOWN, ROUND_UP, Decimal -import django import pytest from django.http import QueryDict from django.test import TestCase, override_settings @@ -1197,11 +1196,6 @@ class TestDateTimeField(FieldValues): field = serializers.DateTimeField(default_timezone=utc) -if django.VERSION[:2] <= (1, 8): - # Doesn't raise an error on earlier versions of Django - TestDateTimeField.invalid_inputs.pop('2018-08-16 22:00-24:00') - - class TestCustomInputFormatDateTimeField(FieldValues): """ Valid and invalid values for `DateTimeField` with a custom input format. diff --git a/tests/test_filters.py b/tests/test_filters.py index 970f6bdfc..f9e068fec 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -1,9 +1,7 @@ from __future__ import unicode_literals import datetime -import unittest -import django import pytest from django.core.exceptions import ImproperlyConfigured from django.db import models @@ -291,7 +289,6 @@ class SearchFilterToManyTests(TestCase): Entry.objects.create(blog=b2, headline='Something unrelated', pub_date=datetime.date(1979, 1, 1)) Entry.objects.create(blog=b2, headline='Retrospective on Lennon', pub_date=datetime.date(1990, 6, 1)) - @unittest.skipIf(django.VERSION < (1, 9), "Django 1.8 does not support transforms") def test_multiple_filter_conditions(self): class SearchListView(generics.ListAPIView): queryset = Blog.objects.all()