diff --git a/.travis.yml b/.travis.yml index fba0fd29..1656f959 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,40 +2,13 @@ language: python sudo: false cache: pip -matrix: - exclude: - - python: "3.3" - env: DJANGO=">=1.9,<1.10" DRF=">=3.3,<3.4" - - python: "3.3" - env: DJANGO=">=1.9,<1.10" DRF=">=3.4,<3.5" - - python: "3.3" - env: DJANGO=">=1.10,<1.11" DRF=">=3.4,<3.5" - - python: "3.3" - env: DJANGO=">=1.11,<1.12" DRF=">=3.4,<3.5" - - python: "3.3" - env: DJANGO=">=1.11,<1.12" DRF=">=3.5,<3.6" - - python: "3.3" - env: DJANGO=">=1.11,<1.12" DRF=">=3.6,<3.7" python: - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" env: - - DJANGO=">=1.8,<1.9" DRF=">=3.1,<3.2" - - DJANGO=">=1.8,<1.9" DRF=">=3.2,<3.3" - - DJANGO=">=1.8,<1.9" DRF=">=3.3,<3.4" - - DJANGO=">=1.8,<1.9" DRF=">=3.4,<3.5" - - - DJANGO=">=1.9,<1.10" DRF=">=3.3,<3.4" - - DJANGO=">=1.9,<1.10" DRF=">=3.4,<3.5" - - - DJANGO=">=1.10,<1.11" DRF=">=3.4,<3.5" - - - DJANGO=">=1.11,<1.12" DRF=">=3.4,<3.5" - - DJANGO=">=1.11,<1.12" DRF=">=3.5,<3.6" - - DJANGO=">=1.11,<1.12" DRF=">=3.6,<3.7" + - DJANGO=">=1.11,<1.12" DRF=">=3.6.3,<3.7" before_install: # Force an upgrade of py & pytest to avoid VersionConflict - pip install --upgrade py diff --git a/AUTHORS b/AUTHORS index b718a77c..f97315ca 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,7 +4,7 @@ Greg Aker Jamie Bliss Jerel Unruh Léo S. -Matt Layman +Matt Layman Ola Tarkowska Oliver Sauder Raphael Cohen diff --git a/CHANGELOG.md b/CHANGELOG.md index 618ad51f..8a6e0aba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +v2.4.0 + +* Drop support for Django 1.8 - 1.10 (EOL) +* Drop support for Django REST Framework < 3.6.3 + (3.6.3 is the first to support Django 1.11) +* Drop support for Python 3.3 (EOL) + v2.3.0 - Released November 28, 2017 * Added support for polymorphic models diff --git a/README.rst b/README.rst index 260ea9d8..cb9a09dc 100644 --- a/README.rst +++ b/README.rst @@ -9,15 +9,10 @@ JSON API and Django Rest Framework :alt: Read the docs :target: http://django-rest-framework-json-api.readthedocs.org/ -.. image:: https://codeclimate.com/github/django-json-api/django-rest-framework-json-api/badges/gpa.svg - :target: https://codeclimate.com/github/django-json-api/django-rest-framework-json-api - :alt: Code Climate - .. image:: https://badges.gitter.im/Join%20Chat.svg :alt: Join the chat at https://gitter.im/django-json-api/django-rest-framework-json-api :target: https://gitter.im/django-json-api/django-rest-framework-json-api - -------- Overview -------- @@ -72,9 +67,9 @@ like the following:: Requirements ------------ -1. Python (2.7, 3.3, 3.4, 3.5, 3.6) -2. Django (1.8, 1.9, 1.10, 1.11) -3. Django REST Framework (3.1, 3.2, 3.3, 3.4, 3.5, 3.6) +1. Python (2.7, 3.4, 3.5, 3.6) +2. Django (1.11) +3. Django REST Framework (3.6) ------------ Installation @@ -94,7 +89,8 @@ From Source :: $ git clone https://github.com/django-json-api/django-rest-framework-json-api.git - $ cd django-rest-framework-json-api && pip install -e . + $ cd django-rest-framework-json-api + $ pip install -e . Running the example app @@ -103,7 +99,8 @@ Running the example app :: $ git clone https://github.com/django-json-api/django-rest-framework-json-api.git - $ cd django-rest-framework-json-api && pip install -e . + $ cd django-rest-framework-json-api + $ pip install -e . $ django-admin.py runserver --settings=example.settings Browse to http://localhost:8000 @@ -112,7 +109,7 @@ Browse to http://localhost:8000 Running Tests ^^^^^^^^^^^^^ -It is recommended to create a virtualenv for testing. Assuming it is already +It is recommended to create a virtualenv for testing. Assuming it is already installed and activated: :: diff --git a/docs/getting-started.md b/docs/getting-started.md index 336f4486..aac1cfc2 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -51,9 +51,9 @@ like the following: ## Requirements -1. Python (2.7, 3.3, 3.4, 3.5, 3.6) -2. Django (1.8, 1.9, 1.10, 1.11) -3. Django REST Framework (3.1, 3.2, 3.3, 3.4, 3.5, 3.6) +1. Python (2.7, 3.4, 3.5, 3.6) +2. Django (1.11) +3. Django REST Framework (3.6) ## Installation diff --git a/rest_framework_json_api/__init__.py b/rest_framework_json_api/__init__.py index 7e35dd7e..b9631b00 100644 --- a/rest_framework_json_api/__init__.py +++ b/rest_framework_json_api/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- __title__ = 'djangorestframework-jsonapi' -__version__ = '2.3.1' +__version__ = '2.4.0' __author__ = '' __license__ = 'MIT' __copyright__ = '' diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index b5b36382..e90b4380 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -5,7 +5,7 @@ import inflection import six from django.core.exceptions import ImproperlyConfigured -from django.core.urlresolvers import NoReverseMatch +from django.urls import NoReverseMatch from django.utils.translation import ugettext_lazy as _ from rest_framework.fields import MISSING_ERROR_MESSAGE from rest_framework.relations import MANY_RELATION_KWARGS, PrimaryKeyRelatedField @@ -291,8 +291,6 @@ def __new__(cls, *args, **kwargs): return super(ResourceRelatedField, cls).__new__(cls, *args, **kwargs) def __init__(self, child_relation=None, *args, **kwargs): - # DRF 3.1 doesn't expect the `many` kwarg - kwargs.pop('many', None) model = kwargs.pop('model', None) if child_relation is not None: self.child_relation = child_relation diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py index 2f1d329a..2f61580e 100644 --- a/rest_framework_json_api/utils.py +++ b/rest_framework_json_api/utils.py @@ -1,56 +1,34 @@ -""" -Utils. -""" import copy import inspect import operator import warnings from collections import OrderedDict -import django import inflection from django.conf import settings from django.db.models import Manager +from django.db.models.fields.related_descriptors import ( + ManyToManyDescriptor, + ReverseManyToOneDescriptor +) from django.utils import encoding, six from django.utils.module_loading import import_string as import_class_from_dotted_path from django.utils.translation import ugettext_lazy as _ from rest_framework import exceptions from rest_framework.exceptions import APIException - -try: - from rest_framework.serializers import ManyRelatedField -except ImportError: - ManyRelatedField = object() +from rest_framework.serializers import ManyRelatedField # noqa: F401 try: from rest_framework_nested.relations import HyperlinkedRouterField except ImportError: HyperlinkedRouterField = object() -if django.VERSION >= (1, 9): - from django.db.models.fields.related_descriptors import ( - ManyToManyDescriptor, ReverseManyToOneDescriptor # noqa: F401 - ) - ReverseManyRelatedObjectsDescriptor = object() -else: - from django.db.models.fields.related import ( # noqa: F401 - ManyRelatedObjectsDescriptor as ManyToManyDescriptor - ) - from django.db.models.fields.related import ( - ForeignRelatedObjectsDescriptor as ReverseManyToOneDescriptor - ) - from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor # noqa: F401 - # Generic relation descriptor from django.contrib.contenttypes. if 'django.contrib.contenttypes' not in settings.INSTALLED_APPS: # pragma: no cover # Target application does not use contenttypes. Importing would cause errors. ReverseGenericManyToOneDescriptor = object() -elif django.VERSION >= (1, 9): - from django.contrib.contenttypes.fields import ReverseGenericManyToOneDescriptor # noqa: F401 else: - from django.contrib.contenttypes.fields import ( # noqa: F401 - ReverseGenericRelatedObjectsDescriptor as ReverseGenericManyToOneDescriptor # noqa: F401 - ) + from django.contrib.contenttypes.fields import ReverseGenericManyToOneDescriptor def get_resource_name(context, expand_polymorphic_types=False): @@ -236,30 +214,14 @@ def get_related_resource_type(relation): parent_model_relation_type = type(parent_model_relation) if parent_model_relation_type is ReverseManyToOneDescriptor: - if django.VERSION >= (1, 9): - relation_model = parent_model_relation.rel.related_model - elif django.VERSION >= (1, 8): - relation_model = parent_model_relation.related.related_model - else: - relation_model = parent_model_relation.related.model + relation_model = parent_model_relation.rel.related_model elif parent_model_relation_type is ManyToManyDescriptor: - if django.VERSION >= (1, 9): - relation_model = parent_model_relation.field.remote_field.model - # In case we are in a reverse relation - if relation_model == parent_model: - relation_model = parent_model_relation.field.model - elif django.VERSION >= (1, 8): - relation_model = parent_model_relation.related.model - # In case we are in a reverse relation - if relation_model == parent_model: - relation_model = parent_model_relation.related.related_model - elif parent_model_relation_type is ReverseManyRelatedObjectsDescriptor: - relation_model = parent_model_relation.field.related.model + relation_model = parent_model_relation.field.remote_field.model + # In case we are in a reverse relation + if relation_model == parent_model: + relation_model = parent_model_relation.field.model elif parent_model_relation_type is ReverseGenericManyToOneDescriptor: - if django.VERSION >= (1, 9): - relation_model = parent_model_relation.rel.model - else: - relation_model = parent_model_relation.field.related_model + relation_model = parent_model_relation.rel.model elif hasattr(parent_model_relation, 'field'): try: relation_model = parent_model_relation.field.remote_field.model diff --git a/rest_framework_json_api/views.py b/rest_framework_json_api/views.py index efb27df7..0156bccc 100644 --- a/rest_framework_json_api/views.py +++ b/rest_framework_json_api/views.py @@ -1,8 +1,14 @@ -import django from django.core.exceptions import ImproperlyConfigured from django.db.models import Model +from django.db.models.fields.related_descriptors import ( + ForwardManyToOneDescriptor, + ManyToManyDescriptor, + ReverseManyToOneDescriptor, + ReverseOneToOneDescriptor +) from django.db.models.manager import Manager from django.db.models.query import QuerySet +from django.urls import NoReverseMatch from rest_framework import generics, viewsets from rest_framework.exceptions import MethodNotAllowed, NotFound from rest_framework.response import Response @@ -18,30 +24,10 @@ get_resource_type_from_instance ) -if django.VERSION >= (1, 10): - from django.urls import NoReverseMatch -else: - from django.core.urlresolvers import NoReverseMatch - -if django.VERSION < (1, 9): - from django.db.models.fields.related import ( - ForeignRelatedObjectsDescriptor as ReverseManyToOneDescriptor, - ManyRelatedObjectsDescriptor as ManyToManyDescriptor, - ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor, - SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor, - ) -else: - from django.db.models.fields.related_descriptors import ( - ForwardManyToOneDescriptor, - ManyToManyDescriptor, - ReverseManyToOneDescriptor, - ReverseOneToOneDescriptor, - ) - class PrefetchForIncludesHelperMixin(object): def get_queryset(self): - """ This viewset provides a helper attribute to prefetch related models + """This viewset provides a helper attribute to prefetch related models based on the include specified in the URL. __all__ can be used to specify a prefetch which should be done regardless of the include @@ -99,10 +85,7 @@ def get_queryset(self, *args, **kwargs): if level == levels[-1]: included_model = field else: - if django.VERSION < (1, 9): - model_field = field.related - else: - model_field = field.field + model_field = field.field if is_forward_relation: level_model = model_field.related_model @@ -197,9 +180,8 @@ def patch(self, request, *args, **kwargs): related_instance_or_manager.all().delete() # have to set bulk to False since data isn't saved yet class_name = related_instance_or_manager.__class__.__name__ - if django.VERSION >= (1, 9) and class_name != 'ManyRelatedManager': - related_instance_or_manager.add(*serializer.validated_data, - bulk=False) + if class_name != 'ManyRelatedManager': + related_instance_or_manager.add(*serializer.validated_data, bulk=False) else: related_instance_or_manager.add(*serializer.validated_data) else: diff --git a/setup.py b/setup.py index d83afa12..4dfe6bed 100755 --- a/setup.py +++ b/setup.py @@ -89,7 +89,6 @@ def get_package_data(package): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', @@ -99,8 +98,8 @@ def get_package_data(package): ], install_requires=[ 'inflection>=0.3.0', - 'djangorestframework>=3.1.0', - 'django', + 'djangorestframework>=3.6.3', + 'django>=1.11', 'six', ], setup_requires=pytest_runner + sphinx + wheel, diff --git a/tox.ini b/tox.ini index cfbdf65f..4b08065f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,22 +1,11 @@ [tox] envlist = - py{27,33,34,35,36}-django18-drf{31,32,33,34}, - py{27,34,35,36}-django19-drf{33,34}, - py{27,34,35,36}-django110-drf34, - py{27,34,35,36}-django111-drf{34,35,36}, + py{27,34,35,36}-django111-drf{36}, [testenv] deps = - django18: Django>=1.8,<1.9 - django19: Django>=1.9,<1.10 - django110: Django>=1.10,<1.11 django111: Django>=1.11,<1.12 - drf31: djangorestframework>=3.1,<3.2 - drf32: djangorestframework>=3.2,<3.3 - drf33: djangorestframework>=3.3,<3.4 - drf34: djangorestframework>=3.4,<3.5 - drf35: djangorestframework>=3.5,<3.6 - drf36: djangorestframework>=3.6,<3.7 + drf36: djangorestframework>=3.6.3,<3.7 setenv = PYTHONPATH = {toxinidir}