diff --git a/rest_framework/compat.py b/rest_framework/compat.py index adce32be8..e435618a2 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -86,18 +86,6 @@ except ImportError: crispy_forms = None -if django.VERSION >= (1, 6): - def clean_manytomany_helptext(text): - return text -else: - # Up to version 1.5 many to many fields automatically suffix - # the `help_text` attribute with hardcoded text. - def clean_manytomany_helptext(text): - if text.endswith(' Hold down "Control", or "Command" on a Mac, to select more than one.'): - text = text[:-69] - return text - - # Django-guardian is optional. Import only if guardian is in INSTALLED_APPS # Fixes (#1712). We keep the try/except for the test suite. guardian = None @@ -109,41 +97,6 @@ except ImportError: pass -# MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+ -if django.VERSION >= (1, 8): - from django.core.validators import MinValueValidator, MaxValueValidator - from django.core.validators import MinLengthValidator, MaxLengthValidator -else: - from django.core.validators import MinValueValidator as DjangoMinValueValidator - from django.core.validators import MaxValueValidator as DjangoMaxValueValidator - from django.core.validators import MinLengthValidator as DjangoMinLengthValidator - from django.core.validators import MaxLengthValidator as DjangoMaxLengthValidator - - - class MinValueValidator(DjangoMinValueValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MinValueValidator, self).__init__(*args, **kwargs) - - - class MaxValueValidator(DjangoMaxValueValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MaxValueValidator, self).__init__(*args, **kwargs) - - - class MinLengthValidator(DjangoMinLengthValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MinLengthValidator, self).__init__(*args, **kwargs) - - - class MaxLengthValidator(DjangoMaxLengthValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(MaxLengthValidator, self).__init__(*args, **kwargs) - - # PATCH method is not implemented by Django if 'patch' not in View.http_method_names: View.http_method_names = View.http_method_names + ['patch'] @@ -188,13 +141,6 @@ else: LONG_SEPARATORS = (b', ', b': ') INDENT_SEPARATORS = (b',', b': ') -if django.VERSION >= (1, 8): - from django.db.models import DurationField - from django.utils.dateparse import parse_duration - from django.utils.duration import duration_string -else: - DurationField = duration_string = parse_duration = None - try: # DecimalValidator is unavailable in Django < 1.9 from django.core.validators import DecimalValidator @@ -223,14 +169,14 @@ def template_render(template, context=None, request=None): """ Passing Context or RequestContext to Template.render is deprecated in 1.9+, see https://github.com/django/django/pull/3883 and - https://github.com/django/django/blob/1.9rc1/django/template/backends/django.py#L82-L84 + https://github.com/django/django/blob/1.9/django/template/backends/django.py#L82-L84 :param template: Template instance :param context: dict :param request: Request instance :return: rendered template as SafeText instance """ - if django.VERSION < (1, 8) or isinstance(template, Template): + if isinstance(template, Template): if request: context = RequestContext(request, context) else: @@ -239,32 +185,3 @@ def template_render(template, context=None, request=None): # backends template, e.g. django.template.backends.django.Template else: return template.render(context, request=request) - - -def get_all_related_objects(opts): - """ - Django 1.8 changed meta api, see - https://docs.djangoproject.com/en/1.8/ref/models/meta/#migrating-old-meta-api - https://code.djangoproject.com/ticket/12663 - https://github.com/django/django/pull/3848 - - :param opts: Options instance - :return: list of relations except many-to-many ones - """ - if django.VERSION < (1, 8): - return opts.get_all_related_objects() - else: - return [r for r in opts.related_objects if not r.field.many_to_many] - - -def get_all_related_many_to_many_objects(opts): - """ - Django 1.8 changed meta api, see docstr in compat.get_all_related_objects() - - :param opts: Options instance - :return: list of many-to-many relations - """ - if django.VERSION < (1, 8): - return opts.get_all_related_many_to_many_objects() - else: - return [r for r in opts.related_objects if r.field.many_to_many] diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 917e412cc..2a08e09ff 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -14,23 +14,23 @@ from django.conf import settings from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ObjectDoesNotExist from django.core.validators import ( - EmailValidator, RegexValidator, URLValidator, ip_address_validators + EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator, + MinValueValidator, RegexValidator, URLValidator, ip_address_validators ) from django.forms import FilePathField as DjangoFilePathField from django.forms import ImageField as DjangoImageField from django.utils import six, timezone -from django.utils.dateparse import parse_date, parse_datetime, parse_time +from django.utils.dateparse import ( + parse_date, parse_datetime, parse_duration, parse_time +) +from django.utils.duration import duration_string from django.utils.encoding import is_protected_type, smart_text from django.utils.functional import cached_property from django.utils.ipv6 import clean_ipv6_address from django.utils.translation import ugettext_lazy as _ from rest_framework import ISO_8601 -from rest_framework.compat import ( - MaxLengthValidator, MaxValueValidator, MinLengthValidator, - MinValueValidator, duration_string, parse_duration, unicode_repr, - unicode_to_repr -) +from rest_framework.compat import unicode_repr, unicode_to_repr from rest_framework.exceptions import ValidationError from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, representation @@ -1217,12 +1217,6 @@ class DurationField(Field): 'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'), } - def __init__(self, *args, **kwargs): - if parse_duration is None: - raise NotImplementedError( - 'DurationField not supported for django versions prior to 1.8') - return super(DurationField, self).__init__(*args, **kwargs) - def to_internal_value(self, value): if isinstance(value, datetime.timedelta): return value diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index 06bc3086f..024f0eb8b 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -11,7 +11,6 @@ from __future__ import unicode_literals import json from collections import OrderedDict -import django from django import forms from django.core.exceptions import ImproperlyConfigured from django.core.paginator import Page @@ -773,7 +772,7 @@ class MultiPartRenderer(BaseRenderer): media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg' format = 'multipart' charset = 'utf-8' - BOUNDARY = 'BoUnDaRyStRiNg' if django.VERSION >= (1, 5) else b'BoUnDaRyStRiNg' + BOUNDARY = 'BoUnDaRyStRiNg' def render(self, data, accepted_media_type=None, renderer_context=None): if hasattr(data, 'items'): diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index b95bb7fa6..bc09df238 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -15,12 +15,12 @@ from __future__ import unicode_literals import warnings from django.db import models +from django.db.models import DurationField as ModelDurationField from django.db.models.fields import Field as DjangoModelField from django.db.models.fields import FieldDoesNotExist from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ -from rest_framework.compat import DurationField as ModelDurationField from rest_framework.compat import JSONField as ModelJSONField from rest_framework.compat import postgres_fields, unicode_to_repr from rest_framework.utils import model_meta diff --git a/rest_framework/test.py b/rest_framework/test.py index e0670365b..3ba4059a9 100644 --- a/rest_framework/test.py +++ b/rest_framework/test.py @@ -4,7 +4,6 @@ # to make it harder for the user to import the wrong thing without realizing. from __future__ import unicode_literals -import django from django.conf import settings from django.test import testcases from django.test.client import Client as DjangoClient @@ -223,9 +222,9 @@ class APITestCase(testcases.TestCase): client_class = APIClient -if django.VERSION >= (1, 4): - class APISimpleTestCase(testcases.SimpleTestCase): - client_class = APIClient +class APISimpleTestCase(testcases.SimpleTestCase): + client_class = APIClient - class APILiveServerTestCase(testcases.LiveServerTestCase): - client_class = APIClient + +class APILiveServerTestCase(testcases.LiveServerTestCase): + client_class = APIClient diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index f151c6f36..c3424a679 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -13,10 +13,6 @@ from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils import six -from rest_framework.compat import ( - get_all_related_many_to_many_objects, get_all_related_objects -) - FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance 'fields', # Dict of field name -> model field instance @@ -138,7 +134,8 @@ def _get_reverse_relationships(opts): # See: https://code.djangoproject.com/ticket/24208 reverse_relations = OrderedDict() - for relation in get_all_related_objects(opts): + 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( @@ -150,7 +147,8 @@ def _get_reverse_relationships(opts): ) # Deal with reverse many-to-many relationships. - for relation in get_all_related_many_to_many_objects(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( diff --git a/tests/test_fields.py b/tests/test_fields.py index 0a878ca79..cefb45605 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -3,7 +3,6 @@ import os import uuid from decimal import Decimal -import django import pytest from django.http import QueryDict from django.utils import six, timezone @@ -969,7 +968,7 @@ class TestDateTimeField(FieldValues): datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()), # Django 1.4 does not support timezone string parsing. - '2001-01-01T14:00+01:00' if (django.VERSION > (1, 4)) else '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()) + '2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()) } invalid_inputs = { 'abc': ['Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z].'], @@ -1095,8 +1094,6 @@ class TestNoOutputFormatTimeField(FieldValues): field = serializers.TimeField(format=None) -@pytest.mark.skipif(django.VERSION < (1, 8), - reason='DurationField is only available for django1.8+') class TestDurationField(FieldValues): """ Valid and invalid values for `DurationField`. @@ -1115,8 +1112,7 @@ class TestDurationField(FieldValues): outputs = { datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123): '3 08:32:01.000123', } - if django.VERSION >= (1, 8): - field = serializers.DurationField() + field = serializers.DurationField() # Choice types... diff --git a/tests/test_generics.py b/tests/test_generics.py index 000adffa7..2e47df50f 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals -import django import pytest from django.db import models from django.shortcuts import get_object_or_404 @@ -158,7 +157,7 @@ class TestRootView(TestCase): self.assertIn(expected_error, response.rendered_content.decode('utf-8')) -EXPECTED_QUERIES_FOR_PUT = 3 if django.VERSION < (1, 6) else 2 +EXPECTED_QUERIES_FOR_PUT = 2 class TestInstanceView(TestCase): diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index f3243e33a..0179abc44 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -10,18 +10,16 @@ from __future__ import unicode_literals import decimal from collections import OrderedDict -import django -import pytest from django.core.exceptions import ImproperlyConfigured from django.core.validators import ( MaxValueValidator, MinLengthValidator, MinValueValidator ) from django.db import models +from django.db.models import DurationField as ModelDurationField from django.test import TestCase from django.utils import six from rest_framework import serializers -from rest_framework.compat import DurationField as ModelDurationField from rest_framework.compat import unicode_repr @@ -341,8 +339,6 @@ class TestRegularFieldMappings(TestCase): assert implicit.data == explicit.data -@pytest.mark.skipif(django.VERSION < (1, 8), - reason='DurationField is only available for django1.8+') class TestDurationFieldMapping(TestCase): def test_duration_field(self): class DurationFieldModel(models.Model): diff --git a/tests/test_request.py b/tests/test_request.py index 24be368b3..7a0575789 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -3,8 +3,6 @@ Tests for content parsing, and form-overloaded content parsing. """ from __future__ import unicode_literals -import django -import pytest from django.conf.urls import url from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User @@ -201,8 +199,6 @@ class TestAuthSetter(TestCase): self.assertEqual(request.auth, 'DUMMY') -@pytest.mark.skipif(django.VERSION < (1, 7), - reason='secure argument is only available for django1.7+') class TestSecure(TestCase): def test_default_secure_false(self):