diff --git a/.travis.yml b/.travis.yml index 87db5e046..4a7fdd42f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,23 +18,16 @@ env: - TOX_ENV=py32-django16 - TOX_ENV=py27-django16 - TOX_ENV=py26-django16 - - TOX_ENV=py34-django15 - - TOX_ENV=py33-django15 - - TOX_ENV=py32-django15 - - TOX_ENV=py27-django15 - - TOX_ENV=py26-django15 - TOX_ENV=py27-djangomaster - - TOX_ENV=py32-djangomaster - - TOX_ENV=py33-djangomaster - TOX_ENV=py34-djangomaster + - TOX_ENV=py35-djangomaster matrix: fast_finish: true allow_failures: - env: TOX_ENV=py27-djangomaster - - env: TOX_ENV=py32-djangomaster - - env: TOX_ENV=py33-djangomaster - env: TOX_ENV=py34-djangomaster + - env: TOX_ENV=py35-djangomaster install: - pip install tox diff --git a/README.md b/README.md index b183e14df..c2a686f4c 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ There is a live example API for testing purposes, [available here][sandbox]. # Requirements * Python (2.6.5+, 2.7, 3.2, 3.3, 3.4) -* Django (1.5.6+, 1.6.3+, 1.7, 1.8) +* Django (1.6.3+, 1.7, 1.8) # Installation diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md index 68803a5b1..be2586f02 100644 --- a/docs/topics/release-notes.md +++ b/docs/topics/release-notes.md @@ -38,6 +38,14 @@ You can determine your currently installed version using `pip freeze`: --- +## 3.3.x series + +### 3.0.0 + +**Date**: NOT YET RELEASED + +* Removed support for Django Version 1.5 ([#3421][gh3421]) + ## 3.2.x series ### 3.2.3 @@ -514,5 +522,6 @@ For older release notes, [please see the version 2.x documentation][old-release- [gh3321]: https://github.com/tomchristie/django-rest-framework/issues/3321 - + +[gh3421]: https://github.com/tomchristie/django-rest-framework/pulls/3421 diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 4d3b29ddd..fcca2dcbf 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -106,17 +106,6 @@ try: except ImportError: django_filters = 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 @@ -127,14 +116,6 @@ except ImportError: pass -def get_model_name(model_cls): - try: - return model_cls._meta.model_name - except AttributeError: - # < 1.6 used module_name instead of model_name - return model_cls._meta.module_name - - # MinValueValidator, MaxValueValidator et al. only accept `message` in 1.8+ if django.VERSION >= (1, 8): from django.core.validators import MinValueValidator, MaxValueValidator @@ -170,32 +151,6 @@ else: super(MaxLengthValidator, self).__init__(*args, **kwargs) -# URLValidator only accepts `message` in 1.6+ -if django.VERSION >= (1, 6): - from django.core.validators import URLValidator -else: - from django.core.validators import URLValidator as DjangoURLValidator - - - class URLValidator(DjangoURLValidator): - def __init__(self, *args, **kwargs): - self.message = kwargs.pop('message', self.message) - super(URLValidator, self).__init__(*args, **kwargs) - - -# EmailValidator requires explicit regex prior to 1.6+ -if django.VERSION >= (1, 6): - from django.core.validators import EmailValidator -else: - from django.core.validators import EmailValidator as DjangoEmailValidator - from django.core.validators import email_re - - - class EmailValidator(DjangoEmailValidator): - def __init__(self, *args, **kwargs): - super(EmailValidator, self).__init__(email_re, *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'] diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7c48c621e..1e23b35f0 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -11,7 +11,9 @@ import uuid from django.conf import settings from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ObjectDoesNotExist -from django.core.validators import RegexValidator, ip_address_validators +from django.core.validators import ( + EmailValidator, 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 @@ -23,9 +25,9 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import ISO_8601 from rest_framework.compat import ( - EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator, - MinValueValidator, OrderedDict, URLValidator, duration_string, - parse_duration, unicode_repr, unicode_to_repr + MaxLengthValidator, MaxValueValidator, MinLengthValidator, + MinValueValidator, OrderedDict, duration_string, parse_duration, + unicode_repr, unicode_to_repr ) from rest_framework.exceptions import ValidationError from rest_framework.settings import api_settings diff --git a/rest_framework/filters.py b/rest_framework/filters.py index 90c19aba0..c1e0ae054 100644 --- a/rest_framework/filters.py +++ b/rest_framework/filters.py @@ -11,9 +11,7 @@ from django.core.exceptions import ImproperlyConfigured from django.db import models from django.utils import six -from rest_framework.compat import ( - distinct, django_filters, get_model_name, guardian -) +from rest_framework.compat import distinct, django_filters, guardian from rest_framework.settings import api_settings FilterSet = django_filters and django_filters.FilterSet or None @@ -202,7 +200,7 @@ class DjangoObjectPermissionsFilter(BaseFilterBackend): model_cls = queryset.model kwargs = { 'app_label': model_cls._meta.app_label, - 'model_name': get_model_name(model_cls) + 'model_name': model_cls._meta.model_name } permission = self.perm_format % kwargs if guardian.VERSION >= (1, 3): diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 628538903..292952cfa 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -5,8 +5,6 @@ from __future__ import unicode_literals from django.http import Http404 -from rest_framework.compat import get_model_name - SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS') @@ -104,7 +102,7 @@ class DjangoModelPermissions(BasePermission): """ kwargs = { 'app_label': model_cls._meta.app_label, - 'model_name': get_model_name(model_cls) + 'model_name': model_cls._meta.model_name } return [perm % kwargs for perm in self.perms_map[method]] @@ -166,7 +164,7 @@ class DjangoObjectPermissions(DjangoModelPermissions): def get_required_object_permissions(self, method, model_cls): kwargs = { 'app_label': model_cls._meta.app_label, - 'model_name': get_model_name(model_cls) + 'model_name': model_cls._meta.model_name } return [perm % kwargs for perm in self.perms_map[method]] diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py index f2598974e..a28553693 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 clean_manytomany_helptext from rest_framework.validators import UniqueValidator NUMERIC_FIELD_TYPES = ( @@ -222,7 +221,7 @@ def get_relation_kwargs(field_name, relation_info): if model_field: if model_field.verbose_name and needs_label(model_field, field_name): kwargs['label'] = capfirst(model_field.verbose_name) - help_text = clean_manytomany_helptext(model_field.help_text) + help_text = model_field.help_text if help_text: kwargs['help_text'] = help_text if not model_field.editable: diff --git a/tests/test_permissions.py b/tests/test_permissions.py index ffc262a41..f0d77e957 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -11,7 +11,7 @@ from rest_framework import ( HTTP_HEADER_ENCODING, authentication, generics, permissions, serializers, status ) -from rest_framework.compat import get_model_name, guardian, unittest +from rest_framework.compat import guardian, unittest from rest_framework.filters import DjangoObjectPermissionsFilter from rest_framework.routers import DefaultRouter from rest_framework.test import APIRequestFactory @@ -278,7 +278,7 @@ class ObjectPermissionsIntegrationTests(TestCase): # give everyone model level permissions, as we are not testing those everyone = Group.objects.create(name='everyone') - model_name = get_model_name(BasicPermModel) + model_name = BasicPermModel._meta.model_name app_label = BasicPermModel._meta.app_label f = '{0}_{1}'.format perms = { diff --git a/tox.ini b/tox.ini index ef505248b..135ca4407 100644 --- a/tox.ini +++ b/tox.ini @@ -4,15 +4,23 @@ addopts=--tb=short [tox] envlist = py27-{lint,docs}, - {py26,py27,py32,py33,py34}-django{15,16}, - {py27,py32,py33,py34}-django{17,18,master} + {py26,py27,py32,py33,py34}-django16, + {py27,py32,py33,py34}-django{17,18}, + {py27,py34,py35}-django{master} [testenv] +basepython = + py26: python2.6 + py27: python2.7 + py32: python3.2 + py33: python3.3 + py34: python3.4 + py35: python3.5 + commands = ./runtests.py --fast {posargs} --coverage setenv = PYTHONDONTWRITEBYTECODE=1 deps = - django15: Django==1.5.6 # Should track minimum supported django16: Django==1.6.3 # Should track minimum supported django17: Django==1.7.10 # Should track maximum supported django18: Django==1.8.4 # Should track maximum supported