diff --git a/.travis.yml b/.travis.yml index 6d6ccfb2..301ed0cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: python +dist: xenial sudo: required cache: pip # Favor explicit over implicit and use an explicit build matrix. @@ -12,6 +13,10 @@ matrix: - env: TOXENV=py35-django22-drfmaster - env: TOXENV=py36-django22-drfmaster - env: TOXENV=py37-django22-drfmaster + - env: TOXENV=py38-django22-drfmaster + - env: TOXENV=py36-django30-drfmaster + - env: TOXENV=py37-django30-drfmaster + - env: TOXENV=py38-django30-drfmaster include: - python: 3.6 @@ -21,50 +26,72 @@ matrix: - python: 3.5 env: TOXENV=py35-django111-drf310 + - python: 3.5 + env: TOXENV=py35-django111-drf311 - python: 3.5 env: TOXENV=py35-django111-drfmaster - python: 3.5 env: TOXENV=py35-django21-drf310 + - python: 3.5 + env: TOXENV=py35-django21-drf311 - python: 3.5 env: TOXENV=py35-django21-drfmaster - python: 3.5 - dist: xenial env: TOXENV=py35-django22-drf310 - python: 3.5 - dist: xenial + env: TOXENV=py35-django22-drf311 + - python: 3.5 env: TOXENV=py35-django22-drfmaster - python: 3.6 env: TOXENV=py36-django111-drf310 + - python: 3.6 + env: TOXENV=py36-django111-drf311 - python: 3.6 env: TOXENV=py36-django111-drfmaster - python: 3.6 env: TOXENV=py36-django21-drf310 + - python: 3.6 + env: TOXENV=py36-django21-drf311 - python: 3.6 env: TOXENV=py36-django21-drfmaster - python: 3.6 - dist: xenial env: TOXENV=py36-django22-drf310 - python: 3.6 - dist: xenial + env: TOXENV=py36-django22-drf311 + - python: 3.6 env: TOXENV=py36-django22-drfmaster + - python: 3.6 + env: TOXENV=py36-django30-drf311 + - python: 3.6 + env: TOXENV=py36-django30-drfmaster - python: 3.7 - dist: xenial - sudo: required env: TOXENV=py37-django21-drf310 - python: 3.7 - dist: xenial - sudo: required + env: TOXENV=py37-django21-drf311 + - python: 3.7 env: TOXENV=py37-django21-drfmaster - python: 3.7 - dist: xenial - sudo: required env: TOXENV=py37-django22-drf310 - python: 3.7 - dist: xenial - sudo: required + env: TOXENV=py37-django22-drf311 + - python: 3.7 env: TOXENV=py37-django22-drfmaster + - python: 3.7 + env: TOXENV=py37-django30-drf311 + - python: 3.7 + env: TOXENV=py37-django30-drfmaster + + - python: 3.8 + env: TOXENV=py38-django22-drf311 + - python: 3.8 + env: TOXENV=py38-django22-drfmaster + - python: 3.8 + env: TOXENV=py38-django30-drf311 + - python: 3.8 + env: TOXENV=py38-django30-drfmaster + install: - pip install tox script: diff --git a/CHANGELOG.md b/CHANGELOG.md index d82de744..eb93ab14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Note that in line with [Django REST Framework policy](http://www.django-rest-framework.org/topics/release-notes/), any parts of the framework not mentioned in the documentation should generally be considered private API, and may be subject to change. +## [Unreleased] + +### Added + +* Added support for Python 3.8 +* Added support for Django REST framework 3.11 +* Added support for Django 3.0 + ## [3.0.0] - 2019-10-14 This release is not backwards compatible. For easy migration best upgrade first to version diff --git a/README.rst b/README.rst index aaa6678c..5541eba9 100644 --- a/README.rst +++ b/README.rst @@ -87,9 +87,9 @@ As a Django REST Framework JSON API (short DJA) we are trying to address followi Requirements ------------ -1. Python (3.5, 3.6, 3.7) -2. Django (1.11, 2.1, 2.2) -3. Django REST Framework (3.10) +1. Python (3.5, 3.6, 3.7, 3.8) +2. Django (1.11, 2.1, 2.2, 3.0) +3. Django REST Framework (3.10, 3.11) We **highly** recommend and only officially support the latest patch release of each Python, Django and REST Framework series. diff --git a/docs/getting-started.md b/docs/getting-started.md index 6f5d60ab..00d77c61 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -51,9 +51,9 @@ like the following: ## Requirements -1. Python (3.5, 3.6, 3.7) -2. Django (1.11, 2.1, 2.2) -3. Django REST Framework (3.10) +1. Python (3.5, 3.6, 3.7, 3.8) +2. Django (1.11, 2.1, 2.2, 3.0) +3. Django REST Framework (3.10, 3.11) We **highly** recommend and only officially support the latest patch release of each Python, Django and REST Framework series. diff --git a/example/api/resources/identity.py b/example/api/resources/identity.py index 5f5a71f2..6785e5d9 100644 --- a/example/api/resources/identity.py +++ b/example/api/resources/identity.py @@ -23,8 +23,8 @@ def posts(self, request): posts = [{'id': 1, 'title': 'Test Blog Post'}] data = { - encoding.force_text('identities'): IdentitySerializer(identities, many=True).data, - encoding.force_text('posts'): PostSerializer(posts, many=True).data, + encoding.force_str('identities'): IdentitySerializer(identities, many=True).data, + encoding.force_str('posts'): PostSerializer(posts, many=True).data, } return Response(utils.format_field_names(data, format_type='camelize')) diff --git a/example/tests/test_format_keys.py b/example/tests/test_format_keys.py index bfa61b54..ba3f4920 100644 --- a/example/tests/test_format_keys.py +++ b/example/tests/test_format_keys.py @@ -28,7 +28,7 @@ def test_camelization(self): 'data': [ { 'type': 'users', - 'id': encoding.force_text(user.pk), + 'id': encoding.force_str(user.pk), 'attributes': { 'firstName': user.first_name, 'lastName': user.last_name, diff --git a/example/tests/test_model_viewsets.py b/example/tests/test_model_viewsets.py index e1a16206..820c8931 100644 --- a/example/tests/test_model_viewsets.py +++ b/example/tests/test_model_viewsets.py @@ -34,7 +34,7 @@ def test_key_in_list_result(self): 'data': [ { 'type': 'users', - 'id': encoding.force_text(user.pk), + 'id': encoding.force_str(user.pk), 'attributes': { 'first-name': user.first_name, 'last-name': user.last_name, @@ -72,7 +72,7 @@ def test_page_two_in_list_result(self): 'data': [ { 'type': 'users', - 'id': encoding.force_text(user.pk), + 'id': encoding.force_str(user.pk), 'attributes': { 'first-name': user.first_name, 'last-name': user.last_name, @@ -112,7 +112,7 @@ def test_page_range_in_list_result(self): 'data': [ { 'type': 'users', - 'id': encoding.force_text(users[0].pk), + 'id': encoding.force_str(users[0].pk), 'attributes': { 'first-name': users[0].first_name, 'last-name': users[0].last_name, @@ -121,7 +121,7 @@ def test_page_range_in_list_result(self): }, { 'type': 'users', - 'id': encoding.force_text(users[1].pk), + 'id': encoding.force_str(users[1].pk), 'attributes': { 'first-name': users[1].first_name, 'last-name': users[1].last_name, @@ -157,7 +157,7 @@ def test_key_in_detail_result(self): expected = { 'data': { 'type': 'users', - 'id': encoding.force_text(self.miles.pk), + 'id': encoding.force_str(self.miles.pk), 'attributes': { 'first-name': self.miles.first_name, 'last-name': self.miles.last_name, @@ -193,7 +193,7 @@ def test_key_in_post(self): data = { 'data': { 'type': 'users', - 'id': encoding.force_text(self.miles.pk), + 'id': encoding.force_str(self.miles.pk), 'attributes': { 'first-name': self.miles.first_name, 'last-name': self.miles.last_name, diff --git a/example/tests/test_sideload_resources.py b/example/tests/test_sideload_resources.py index 4c9c1525..69641af7 100644 --- a/example/tests/test_sideload_resources.py +++ b/example/tests/test_sideload_resources.py @@ -25,5 +25,5 @@ def test_get_sideloaded_data(self): self.assertEqual( sorted(content.keys()), - [encoding.force_text('identities'), - encoding.force_text('posts')]) + [encoding.force_str('identities'), + encoding.force_str('posts')]) diff --git a/rest_framework_json_api/exceptions.py b/rest_framework_json_api/exceptions.py index 38ff527b..938a0c77 100644 --- a/rest_framework_json_api/exceptions.py +++ b/rest_framework_json_api/exceptions.py @@ -1,4 +1,4 @@ -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions, status from rest_framework_json_api import utils diff --git a/rest_framework_json_api/metadata.py b/rest_framework_json_api/metadata.py index ed0a86ce..ef3356fe 100644 --- a/rest_framework_json_api/metadata.py +++ b/rest_framework_json_api/metadata.py @@ -1,7 +1,7 @@ from collections import OrderedDict from django.db.models.fields import related -from django.utils.encoding import force_text +from django.utils.encoding import force_str from rest_framework import serializers from rest_framework.metadata import SimpleMetadata from rest_framework.settings import api_settings @@ -123,7 +123,7 @@ def get_field_info(self, field): for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': - field_info[attr] = force_text(value, strings_only=True) + field_info[attr] = force_str(value, strings_only=True) if getattr(field, 'child', None): field_info['child'] = self.get_field_info(field.child) @@ -138,7 +138,7 @@ def get_field_info(self, field): field_info['choices'] = [ { 'value': choice_value, - 'display_name': force_text(choice_name, strings_only=True) + 'display_name': force_str(choice_name, strings_only=True) } for choice_value, choice_name in field.choices.items() ] diff --git a/rest_framework_json_api/relations.py b/rest_framework_json_api/relations.py index 54226e5a..9fbfb98f 100644 --- a/rest_framework_json_api/relations.py +++ b/rest_framework_json_api/relations.py @@ -5,7 +5,7 @@ import inflection from django.core.exceptions import ImproperlyConfigured from django.urls import NoReverseMatch -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework.fields import MISSING_ERROR_MESSAGE, SkipField from rest_framework.relations import MANY_RELATION_KWARGS from rest_framework.relations import ManyRelatedField as DRFManyRelatedField diff --git a/rest_framework_json_api/renderers.py b/rest_framework_json_api/renderers.py index ced826b0..8da333ed 100644 --- a/rest_framework_json_api/renderers.py +++ b/rest_framework_json_api/renderers.py @@ -129,7 +129,7 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_data.append( OrderedDict([ ('type', relation_type), - ('id', encoding.force_text(related_object.pk)) + ('id', encoding.force_str(related_object.pk)) ]) ) @@ -168,7 +168,7 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_data = { 'data': ( OrderedDict([ - ('type', relation_type), ('id', encoding.force_text(relation_id)) + ('type', relation_type), ('id', encoding.force_str(relation_id)) ]) if relation_id is not None else None) } @@ -231,7 +231,7 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_data.append(OrderedDict([ ('type', nested_resource_instance_type), - ('id', encoding.force_text(nested_resource_instance.pk)) + ('id', encoding.force_str(nested_resource_instance.pk)) ])) data.update({ field_name: { @@ -264,7 +264,7 @@ def extract_relationships(cls, fields, resource, resource_instance): relation_data.append(OrderedDict([ ('type', nested_resource_instance_type), - ('id', encoding.force_text(nested_resource_instance.pk)) + ('id', encoding.force_str(nested_resource_instance.pk)) ])) data.update({field_name: {'data': relation_data}}) @@ -287,7 +287,7 @@ def extract_relationships(cls, fields, resource, resource_instance): 'data': ( OrderedDict([ ('type', relation_type), - ('id', encoding.force_text(relation_instance_id)) + ('id', encoding.force_str(relation_instance_id)) ]) if resource.get(field_name) else None) } }) @@ -486,7 +486,7 @@ def build_json_resource_obj(cls, fields, resource, resource_instance, resource_n resource_name = utils.get_resource_type_from_instance(resource_instance) resource_data = [ ('type', resource_name), - ('id', encoding.force_text(resource_instance.pk) if resource_instance else None), + ('id', encoding.force_str(resource_instance.pk) if resource_instance else None), ('attributes', cls.extract_attributes(fields, resource)), ] relationships = cls.extract_relationships(fields, resource, resource_instance) diff --git a/rest_framework_json_api/serializers.py b/rest_framework_json_api/serializers.py index be0dcace..5f7a31e8 100644 --- a/rest_framework_json_api/serializers.py +++ b/rest_framework_json_api/serializers.py @@ -1,7 +1,7 @@ import inflection from django.core.exceptions import ObjectDoesNotExist from django.db.models.query import QuerySet -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework.exceptions import ParseError from rest_framework.serializers import * # noqa: F403 diff --git a/rest_framework_json_api/utils.py b/rest_framework_json_api/utils.py index dd92ba23..90b9116b 100644 --- a/rest_framework_json_api/utils.py +++ b/rest_framework_json_api/utils.py @@ -13,7 +13,7 @@ from django.http import Http404 from django.utils import encoding from django.utils.module_loading import import_string as import_class_from_dotted_path -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from rest_framework import exceptions from rest_framework.exceptions import APIException @@ -341,7 +341,7 @@ def format_drf_errors(response, context, exc): def format_error_object(message, pointer, response): error_obj = { 'detail': message, - 'status': encoding.force_text(response.status_code), + 'status': encoding.force_str(response.status_code), } if pointer is not None: error_obj['source'] = { diff --git a/tox.ini b/tox.ini index 13992946..04b970ac 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,9 @@ [tox] envlist = - py{35,36}-django111-drf{310,master}, - py{35,36,37}-django{21,22}-drf{310,master}, + py{35,36}-django{111}-drf{310,311,master}, + py{35,36,37}-django{21,22}-drf{310,311,master}, + py38-django22-drf{311,master}, + py{36,37,38}-django{30}-drf{311,master}, lint,docs [testenv] @@ -9,7 +11,9 @@ deps = django111: Django>=1.11,<1.12 django21: Django>=2.1,<2.2 django22: Django>=2.2,<2.3 + django30: Django>=3.0,<3.1 drf310: djangorestframework>=3.10.2,<3.11 + drf311: djangorestframework>=3.11,<3.12 drfmaster: https://github.com/encode/django-rest-framework/archive/master.zip -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt