From c950b7c43cc57493ead1ecabbe7ccb5d1db4848a Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Sun, 1 Oct 2017 21:06:11 -0400 Subject: [PATCH 1/9] Identify code that needs to be pulled out of/removed from compat.py --- rest_framework/compat.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index e0f718ced..614745a66 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -78,6 +78,7 @@ def distinct(queryset, base): return queryset.distinct() +# TODO: Remove # Obtaining manager instances and names from model options differs after 1.10. def get_names_and_managers(options): if django.VERSION >= (1, 10): @@ -96,6 +97,7 @@ def get_names_and_managers(options): ] +# TODO: Remove # field.rel is deprecated from 1.9 onwards def get_remote_field(field, **kwargs): if 'default' in kwargs: @@ -132,30 +134,35 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) +# TODO: Remove def is_authenticated(user): if django.VERSION < (1, 10): return user.is_authenticated() return user.is_authenticated +# TODO: Remove def is_anonymous(user): if django.VERSION < (1, 10): return user.is_anonymous() return user.is_anonymous +# TODO: Remove def get_related_model(field): if django.VERSION < (1, 9): return _resolve_model(field.rel.to) return field.remote_field.model +# TODO: Remove def value_from_object(field, obj): if django.VERSION < (1, 9): return field._get_val_from_obj(obj) return field.value_from_object(obj) +# TODO: Remove # contrib.postgres only supported from 1.8 onwards. try: from django.contrib.postgres import fields as postgres_fields @@ -163,6 +170,7 @@ except ImportError: postgres_fields = None +# TODO: Remove # JSONField is only supported from 1.9 onwards try: from django.contrib.postgres.fields import JSONField @@ -292,6 +300,7 @@ else: LONG_SEPARATORS = (b', ', b': ') INDENT_SEPARATORS = (b',', b': ') +# TODO: Remove try: # DecimalValidator is unavailable in Django < 1.9 from django.core.validators import DecimalValidator @@ -338,6 +347,7 @@ def set_rollback(): pass +# TODO: Remove def template_render(template, context=None, request=None): """ Passing Context or RequestContext to Template.render is deprecated in 1.9+, @@ -360,6 +370,7 @@ def template_render(template, context=None, request=None): return template.render(context, request=request) +# TODO: Remove def set_many(instance, field, value): if django.VERSION < (1, 10): setattr(instance, field, value) @@ -368,6 +379,7 @@ def set_many(instance, field, value): field.set(value) +# TODO: Remove def include(module, namespace=None, app_name=None): from django.conf.urls import include if django.VERSION < (1,9): From 3a43224fed042f6ed14de54cf2ec9119157a727d Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Mon, 2 Oct 2017 20:06:28 -0400 Subject: [PATCH 2/9] Extract modern code from get_names_and_managers in compat.py and remove compat code --- rest_framework/compat.py | 19 ------------------- rest_framework/utils/representation.py | 9 +++++++-- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index abf1d7136..e2acf1c6b 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -78,25 +78,6 @@ def distinct(queryset, base): return queryset.distinct() -# TODO: Remove -# Obtaining manager instances and names from model options differs after 1.10. -def get_names_and_managers(options): - if django.VERSION >= (1, 10): - # Django 1.10 onwards provides a `.managers` property on the Options. - return [ - (manager.name, manager) - for manager - in options.managers - ] - # For Django 1.8 and 1.9, use the three-tuple information provided - # by .concrete_managers and .abstract_managers - return [ - (manager_info[1], manager_info[2]) - for manager_info - in (options.concrete_managers + options.abstract_managers) - ] - - # TODO: Remove # field.rel is deprecated from 1.9 onwards def get_remote_field(field, **kwargs): diff --git a/rest_framework/utils/representation.py b/rest_framework/utils/representation.py index 32e6d246a..deeaf1f63 100644 --- a/rest_framework/utils/representation.py +++ b/rest_framework/utils/representation.py @@ -10,13 +10,18 @@ from django.db import models from django.utils.encoding import force_text from django.utils.functional import Promise -from rest_framework.compat import get_names_and_managers, unicode_repr +from rest_framework.compat import unicode_repr def manager_repr(value): model = value.model opts = model._meta - for manager_name, manager_instance in get_names_and_managers(opts): + names_and_managers = [ + (manager.name, manager) + for manager + in opts.managers + ] + for manager_name, manager_instance in names_and_managers: if manager_instance == value: return '%s.%s.all()' % (model._meta.object_name, manager_name) return repr(value) From 0d2f8d3b3ca0dcff689d58a0be6e61f8f46c9d75 Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Tue, 3 Oct 2017 21:33:12 -0400 Subject: [PATCH 3/9] Extract modern code from is_authenticated() in compat.py and remove. --- rest_framework/compat.py | 7 ------- rest_framework/permissions.py | 7 +++---- rest_framework/throttling.py | 7 +++---- tests/test_authentication.py | 3 +-- tests/test_requests_client.py | 4 ++-- 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index e2acf1c6b..b3db7e9b8 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,13 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) -# TODO: Remove -def is_authenticated(user): - if django.VERSION < (1, 10): - return user.is_authenticated() - return user.is_authenticated - - # TODO: Remove def is_anonymous(user): if django.VERSION < (1, 10): diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index dee0032f9..a48058e66 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals from django.http import Http404 from rest_framework import exceptions -from rest_framework.compat import is_authenticated SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') @@ -47,7 +46,7 @@ class IsAuthenticated(BasePermission): """ def has_permission(self, request, view): - return request.user and is_authenticated(request.user) + return request.user and request.user.is_authenticated class IsAdminUser(BasePermission): @@ -68,7 +67,7 @@ class IsAuthenticatedOrReadOnly(BasePermission): return ( request.method in SAFE_METHODS or request.user and - is_authenticated(request.user) + request.user.is_authenticated ) @@ -136,7 +135,7 @@ class DjangoModelPermissions(BasePermission): return True if not request.user or ( - not is_authenticated(request.user) and self.authenticated_users_only): + not request.user.is_authenticated and self.authenticated_users_only): return False queryset = self._queryset(view) diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py index 57f24d13f..422431566 100644 --- a/rest_framework/throttling.py +++ b/rest_framework/throttling.py @@ -8,7 +8,6 @@ import time from django.core.cache import cache as default_cache from django.core.exceptions import ImproperlyConfigured -from rest_framework.compat import is_authenticated from rest_framework.settings import api_settings @@ -174,7 +173,7 @@ class AnonRateThrottle(SimpleRateThrottle): scope = 'anon' def get_cache_key(self, request, view): - if is_authenticated(request.user): + if request.user.is_authenticated: return None # Only throttle unauthenticated requests. return self.cache_format % { @@ -194,7 +193,7 @@ class UserRateThrottle(SimpleRateThrottle): scope = 'user' def get_cache_key(self, request, view): - if is_authenticated(request.user): + if request.user.is_authenticated: ident = request.user.pk else: ident = self.get_ident(request) @@ -242,7 +241,7 @@ class ScopedRateThrottle(SimpleRateThrottle): Otherwise generate the unique cache key by concatenating the user id with the '.throttle_scope` property of the view. """ - if is_authenticated(request.user): + if request.user.is_authenticated: ident = request.user.pk else: ident = self.get_ident(request) diff --git a/tests/test_authentication.py b/tests/test_authentication.py index fdbc28a2a..8149ddd81 100644 --- a/tests/test_authentication.py +++ b/tests/test_authentication.py @@ -20,7 +20,6 @@ from rest_framework.authentication import ( TokenAuthentication) from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import obtain_auth_token -from rest_framework.compat import is_authenticated from rest_framework.response import Response from rest_framework.test import APIClient, APIRequestFactory from rest_framework.views import APIView @@ -450,7 +449,7 @@ class FailingAuthAccessedInRenderer(TestCase): def render(self, data, media_type=None, renderer_context=None): request = renderer_context['request'] - if is_authenticated(request.user): + if request.user.is_authenticated: return b'authenticated' return b'not authenticated' diff --git a/tests/test_requests_client.py b/tests/test_requests_client.py index 791ca4ff2..161429f73 100644 --- a/tests/test_requests_client.py +++ b/tests/test_requests_client.py @@ -10,7 +10,7 @@ from django.test import override_settings from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie -from rest_framework.compat import is_authenticated, requests +from rest_framework.compat import requests from rest_framework.response import Response from rest_framework.test import APITestCase, RequestsClient from rest_framework.views import APIView @@ -72,7 +72,7 @@ class SessionView(APIView): class AuthView(APIView): @method_decorator(ensure_csrf_cookie) def get(self, request): - if is_authenticated(request.user): + if request.user.is_authenticated: username = request.user.username else: username = None From bb2efdb74ba0fa519f05c031b3747bc05fb93d4e Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Tue, 3 Oct 2017 21:43:02 -0400 Subject: [PATCH 4/9] Extract modern code from is_anonymous() in compat.py and remove --- rest_framework/compat.py | 7 ------- tests/test_request.py | 5 ++--- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index b3db7e9b8..42a22f749 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,13 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) -# TODO: Remove -def is_anonymous(user): - if django.VERSION < (1, 10): - return user.is_anonymous() - return user.is_anonymous - - # TODO: Remove def get_related_model(field): if django.VERSION < (1, 9): diff --git a/tests/test_request.py b/tests/test_request.py index 208d2737e..a87060df1 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -16,7 +16,6 @@ from django.utils import six from rest_framework import status from rest_framework.authentication import SessionAuthentication -from rest_framework.compat import is_anonymous from rest_framework.parsers import BaseParser, FormParser, MultiPartParser from rest_framework.request import Request from rest_framework.response import Response @@ -201,9 +200,9 @@ class TestUserSetter(TestCase): def test_user_can_logout(self): self.request.user = self.user - self.assertFalse(is_anonymous(self.request.user)) + self.assertFalse(self.request.user.is_anonymous) logout(self.request) - self.assertTrue(is_anonymous(self.request.user)) + self.assertTrue(self.request.user.is_anonymous) def test_logged_in_user_is_set_on_wrapped_request(self): login(self.request, self.user) From e2aa256d7604a8edf3bbcb2b691f5f776421c8a6 Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Tue, 3 Oct 2017 21:48:58 -0400 Subject: [PATCH 5/9] Extract modern code from get_related_model() from compat.py and remove --- rest_framework/compat.py | 7 ------- rest_framework/utils/model_meta.py | 8 ++++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 42a22f749..dcf32acfb 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,13 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) -# TODO: Remove -def get_related_model(field): - if django.VERSION < (1, 9): - return _resolve_model(field.rel.to) - return field.remote_field.model - - # TODO: Remove def value_from_object(field, obj): if django.VERSION < (1, 9): diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 3e3e434e6..4f47c2c36 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -7,7 +7,7 @@ Usage: `get_field_info(model)` returns a `FieldInfo` instance. """ from collections import OrderedDict, namedtuple -from rest_framework.compat import get_related_model, get_remote_field +from rest_framework.compat import get_remote_field FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance @@ -53,7 +53,7 @@ def _get_pk(opts): while rel and rel.parent_link: # If model is a child via multi-table inheritance, use parent's pk. - pk = get_related_model(pk)._meta.pk + pk = pk.remote_field.model._meta.pk rel = get_remote_field(pk) return pk @@ -79,7 +79,7 @@ def _get_forward_relationships(opts): for field in [field for field in opts.fields if field.serialize and get_remote_field(field)]: forward_relations[field.name] = RelationInfo( model_field=field, - related_model=get_related_model(field), + related_model=field.remote_field.model, to_many=False, to_field=_get_to_field(field), has_through_model=False, @@ -90,7 +90,7 @@ def _get_forward_relationships(opts): for field in [field for field in opts.many_to_many if field.serialize]: forward_relations[field.name] = RelationInfo( model_field=field, - related_model=get_related_model(field), + related_model=field.remote_field.model, to_many=True, # manytomany do not have to_fields to_field=None, From de202e4acddbecf924b312a10061e05ccedbd88a Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Wed, 4 Oct 2017 19:20:54 -0400 Subject: [PATCH 6/9] Extract modern code from value_from_object() in compat.py and remove --- rest_framework/compat.py | 6 ------ rest_framework/fields.py | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index dcf32acfb..14f2ae6e5 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,12 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) -# TODO: Remove -def value_from_object(field, obj): - if django.VERSION < (1, 9): - return field._get_val_from_obj(obj) - return field.value_from_object(obj) - # TODO: Remove # contrib.postgres only supported from 1.8 onwards. diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7a79ae93c..8fae159ab 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -33,7 +33,7 @@ from rest_framework import ISO_8601 from rest_framework.compat import ( InvalidTimeError, MaxLengthValidator, MaxValueValidator, MinLengthValidator, MinValueValidator, get_remote_field, unicode_repr, - unicode_to_repr, value_from_object + unicode_to_repr ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -1840,7 +1840,7 @@ class ModelField(Field): return obj def to_representation(self, obj): - value = value_from_object(self.model_field, obj) + value = self.model_field.value_from_object(obj) if is_protected_type(value): return value return self.model_field.value_to_string(obj) From 99d120b2e4e281f08fe21709074455f63a618993 Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Wed, 4 Oct 2017 20:45:27 -0400 Subject: [PATCH 7/9] Move postgres_fields import out of compat.py --- rest_framework/compat.py | 9 --------- rest_framework/serializers.py | 8 +++++++- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 14f2ae6e5..914beab77 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,15 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) - -# TODO: Remove -# contrib.postgres only supported from 1.8 onwards. -try: - from django.contrib.postgres import fields as postgres_fields -except ImportError: - postgres_fields = None - - # TODO: Remove # JSONField is only supported from 1.9 onwards try: diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index b1c34b92a..9a231830e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -28,7 +28,7 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from rest_framework.compat import JSONField as ModelJSONField -from rest_framework.compat import postgres_fields, set_many, unicode_to_repr +from rest_framework.compat import set_many, unicode_to_repr from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.fields import get_error_detail, set_value from rest_framework.settings import api_settings @@ -46,6 +46,12 @@ from rest_framework.validators import ( UniqueTogetherValidator ) +try: + from django.contrib.postgres import fields as postgres_fields +except ImportError: + postgres_fields = None + + # Note: We do the following so that users of the framework can use this style: # # example_field = serializers.CharField(...) From a22b47fb5fcdd9840d08acb7cfcc65075422b11d Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Wed, 4 Oct 2017 20:57:52 -0400 Subject: [PATCH 8/9] Move JSONField import out of compat.py --- rest_framework/compat.py | 8 -------- rest_framework/serializers.py | 6 +++++- rest_framework/utils/field_mapping.py | 7 ++++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 914beab77..2e0c0fc16 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -115,14 +115,6 @@ def _resolve_model(obj): raise ValueError("{0} is not a Django model".format(obj)) -# TODO: Remove -# JSONField is only supported from 1.9 onwards -try: - from django.contrib.postgres.fields import JSONField -except ImportError: - JSONField = None - - # coreapi is optional (Note that uritemplate is a dependency of coreapi) try: import coreapi diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 9a231830e..3665bf0a9 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -27,7 +27,6 @@ from django.utils import six, timezone from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import JSONField as ModelJSONField from rest_framework.compat import set_many, unicode_to_repr from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.fields import get_error_detail, set_value @@ -51,6 +50,11 @@ try: except ImportError: postgres_fields = None +try: + from django.contrib.postgres.fields import JSONField as ModelJSONField +except ImportError: + ModelJSONField = None + # Note: We do the following so that users of the framework can use this style: # diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index dff33d8b3..0503a701c 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -8,9 +8,14 @@ from django.core import validators from django.db import models from django.utils.text import capfirst -from rest_framework.compat import DecimalValidator, JSONField +from rest_framework.compat import DecimalValidator from rest_framework.validators import UniqueValidator +try: + from django.contrib.postgres.fields import JSONField +except ImportError: + JSONField = None + NUMERIC_FIELD_TYPES = ( models.IntegerField, models.FloatField, models.DecimalField ) From 6e007b8c5270c74034b59a14e80d033aec45b4b4 Mon Sep 17 00:00:00 2001 From: Levi Payne Date: Wed, 4 Oct 2017 21:09:09 -0400 Subject: [PATCH 9/9] Remove DecimalValidator import from compat.py --- rest_framework/compat.py | 6 ------ rest_framework/utils/field_mapping.py | 5 ++--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 2e0c0fc16..03e7ce3f9 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -270,12 +270,6 @@ else: LONG_SEPARATORS = (b', ', b': ') INDENT_SEPARATORS = (b',', b': ') -# TODO: Remove -try: - # DecimalValidator is unavailable in Django < 1.9 - from django.core.validators import DecimalValidator -except ImportError: - DecimalValidator = None class CustomValidatorMessage(object): """ diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index 0503a701c..e0d23e407 100644 --- a/rest_framework/utils/field_mapping.py +++ b/rest_framework/utils/field_mapping.py @@ -8,7 +8,6 @@ from django.core import validators from django.db import models from django.utils.text import capfirst -from rest_framework.compat import DecimalValidator from rest_framework.validators import UniqueValidator try: @@ -187,10 +186,10 @@ def get_field_kwargs(field_name, model_field): ] # Our decimal validation is handled in the field code, not validator code. # (In Django 1.9+ this differs from previous style) - if isinstance(model_field, models.DecimalField) and DecimalValidator: + if isinstance(model_field, models.DecimalField): validator_kwarg = [ validator for validator in validator_kwarg - if not isinstance(validator, DecimalValidator) + if not isinstance(validator, validators.DecimalValidator) ] # Ensure that max_length is passed explicitly as a keyword arg,