From 97e0830c5b216ce6b8812b4e701df3d804b7c444 Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Wed, 21 Sep 2022 08:08:12 -0500 Subject: [PATCH] Version 3.14.0 proposal (#8599) * Version 3.14.0 * Update docs/community/release-notes.md to use proper links. Co-authored-by: Adam Johnson * Add community announcement page for version 3.14 * Remove deprecated NullBooleanField. * Change openapi _get_reference removal to 3.15 This deprecation was never released in the 3.13.x series and therefore can't be removed at the same time the replacement is released. * Removing deprecated openapi methods. Co-authored-by: Adam Johnson --- docs/api-guide/fields.md | 8 --- docs/community/3.14-announcement.md | 62 +++++++++++++++++ docs/community/release-notes.md | 17 +++++ rest_framework/__init__.py | 6 +- rest_framework/fields.py | 20 +----- rest_framework/metadata.py | 1 - rest_framework/schemas/openapi.py | 102 +--------------------------- rest_framework/serializers.py | 2 +- tests/test_fields.py | 14 +--- 9 files changed, 91 insertions(+), 141 deletions(-) create mode 100644 docs/community/3.14-announcement.md diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index e9ef5c6b642..1e943621329 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -159,14 +159,6 @@ Corresponds to `django.db.models.fields.BooleanField`. **Signature:** `BooleanField()` -## NullBooleanField - -A boolean representation that also accepts `None` as a valid value. - -Corresponds to `django.db.models.fields.NullBooleanField`. - -**Signature:** `NullBooleanField()` - --- # String fields diff --git a/docs/community/3.14-announcement.md b/docs/community/3.14-announcement.md new file mode 100644 index 00000000000..04d1572b1c4 --- /dev/null +++ b/docs/community/3.14-announcement.md @@ -0,0 +1,62 @@ + + +# Django REST framework 3.14 + +## Django 4.1 support + +The latest release now fully supports Django 4.1. + +Our requirements are now: + +* Python 3.6+ +* Django 4.1, 4.0, 3.2, 3.1, 2.2 (LTS) + +## `raise_exceptions` argument for `is_valid` is now keyword-only. + +Calling `serializer_instance.is_valid(True)` is no longer acceptable syntax. +If you'd like to use the `raise_exceptions` argument, you must use it as a +keyword argument. + +See Pull Request [#7952](https://github.com/encode/django-rest-framework/pull/7952) for more details. + +## `ManyRelatedField` supports returning the default when the source attribute doesn't exist. + +Previously, if you used a serializer field with `many=True` with a dot notated source field +that didn't exist, it would raise an `AttributeError`. Now it will return the default or be +skipped depending on the other arguments. + +See Pull Request [#7574](https://github.com/encode/django-rest-framework/pull/7574) for more details. + + +## Make Open API `get_reference` public. + +Returns a reference to the serializer component. This may be useful if you override `get_schema()`. + +## Change semantic of OR of two permission classes. + +When OR-ing two permissions, the request has to pass either class's `has_permission() and has_object_permission()`. + +Previously, both class's `has_permission()` was ignored when OR-ing two permissions together. + +See Pull Request [#7522](https://github.com/encode/django-rest-framework/pull/7522) for more details. + +## Minor fixes and improvements + +There are a number of minor fixes and improvements in this release. See the [release notes](release-notes.md) page for a complete listing. diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 8629e4fee70..dc822703519 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -34,6 +34,23 @@ You can determine your currently installed version using `pip show`: --- +## 3.14.x series + +### 3.14.0 + +Date: 10th August 2022 + +* Enforce `is_valid(raise_exception=False)` as a keyword-only argument. [[#7952](https://github.com/encode/django-rest-framework/pull/7952)] +* Django 4.1 compatability. [[#8591](https://github.com/encode/django-rest-framework/pull/8591)] +* Stop calling `set_context` on Validators. [[#8589](https://github.com/encode/django-rest-framework/pull/8589)] +* Return `NotImplemented` from `ErrorDetails.__ne__`. [[#8538](https://github.com/encode/django-rest-framework/pull/8538)] +* Don't evaluate `DateTimeField.default_timezone` when a custom timezone is set. [[#8531](https://github.com/encode/django-rest-framework/pull/8531)] +* Make relative URLs clickable in Browseable API. [[#8464](https://github.com/encode/django-rest-framework/pull/8464)] +* Support `ManyRelatedField` falling back to the default value when the attribute specified by dot notation doesn't exist. Matches `ManyRelatedField.get_attribute` to `Field.get_attribute`. [[#7574](https://github.com/encode/django-rest-framework/pull/7574)] +* Make `schemas.openapi.get_reference` public. [[#7515](https://github.com/encode/django-rest-framework/pull/7515)] +* Make `ReturnDict` support `dict` union operators on Python 3.9 and later. [[#8302](https://github.com/encode/django-rest-framework/pull/8302)] +* Update throttling to check if `request.user` is set before checking if the user is authenticated. [[#8370](https://github.com/encode/django-rest-framework/pull/8370)] + ## 3.13.x series ### 3.13.1 diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 8b0679ef95b..568e080744b 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -10,7 +10,7 @@ import django __title__ = 'Django REST framework' -__version__ = '3.13.1' +__version__ = '3.14.0' __author__ = 'Tom Christie' __license__ = 'BSD 3-Clause' __copyright__ = 'Copyright 2011-2019 Encode OSS Ltd' @@ -35,3 +35,7 @@ class RemovedInDRF313Warning(DeprecationWarning): class RemovedInDRF314Warning(PendingDeprecationWarning): pass + + +class RemovedInDRF315Warning(PendingDeprecationWarning): + pass diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 72bfe614d92..6374c1ea987 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -5,7 +5,6 @@ import inspect import re import uuid -import warnings from collections import OrderedDict from collections.abc import Mapping @@ -30,7 +29,7 @@ from django.utils.translation import gettext_lazy as _ from pytz.exceptions import InvalidTimeError -from rest_framework import ISO_8601, RemovedInDRF314Warning +from rest_framework import ISO_8601 from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings from rest_framework.utils import html, humanize_datetime, json, representation @@ -712,23 +711,6 @@ def to_representation(self, value): return bool(value) -class NullBooleanField(BooleanField): - initial = None - - def __init__(self, **kwargs): - warnings.warn( - "The `NullBooleanField` is deprecated and will be removed starting " - "with 3.14. Instead use the `BooleanField` field and set " - "`allow_null=True` which does the same thing.", - RemovedInDRF314Warning, stacklevel=2 - ) - - assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.' - kwargs['allow_null'] = True - - super().__init__(**kwargs) - - # String types... class CharField(Field): diff --git a/rest_framework/metadata.py b/rest_framework/metadata.py index 8a44f2aad38..7708fe0a21c 100644 --- a/rest_framework/metadata.py +++ b/rest_framework/metadata.py @@ -36,7 +36,6 @@ class SimpleMetadata(BaseMetadata): label_lookup = ClassLookupDict({ serializers.Field: 'field', serializers.BooleanField: 'boolean', - serializers.NullBooleanField: 'boolean', serializers.CharField: 'string', serializers.UUIDField: 'string', serializers.URLField: 'url', diff --git a/rest_framework/schemas/openapi.py b/rest_framework/schemas/openapi.py index 122846376f2..ee614fdf6ea 100644 --- a/rest_framework/schemas/openapi.py +++ b/rest_framework/schemas/openapi.py @@ -13,7 +13,7 @@ from django.utils.encoding import force_str from rest_framework import ( - RemovedInDRF314Warning, exceptions, renderers, serializers + RemovedInDRF315Warning, exceptions, renderers, serializers ) from rest_framework.compat import uritemplate from rest_framework.fields import _UnvalidatedField, empty @@ -713,106 +713,10 @@ def get_tags(self, path, method): return [path.split('/')[0].replace('_', '-')] - def _get_path_parameters(self, path, method): - warnings.warn( - "Method `_get_path_parameters()` has been renamed to `get_path_parameters()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_path_parameters(path, method) - - def _get_filter_parameters(self, path, method): - warnings.warn( - "Method `_get_filter_parameters()` has been renamed to `get_filter_parameters()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_filter_parameters(path, method) - - def _get_responses(self, path, method): - warnings.warn( - "Method `_get_responses()` has been renamed to `get_responses()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_responses(path, method) - - def _get_request_body(self, path, method): - warnings.warn( - "Method `_get_request_body()` has been renamed to `get_request_body()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_request_body(path, method) - - def _get_serializer(self, path, method): - warnings.warn( - "Method `_get_serializer()` has been renamed to `get_serializer()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_serializer(path, method) - - def _get_paginator(self): - warnings.warn( - "Method `_get_paginator()` has been renamed to `get_paginator()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_paginator() - - def _map_field_validators(self, field, schema): - warnings.warn( - "Method `_map_field_validators()` has been renamed to `map_field_validators()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.map_field_validators(field, schema) - - def _map_serializer(self, serializer): - warnings.warn( - "Method `_map_serializer()` has been renamed to `map_serializer()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.map_serializer(serializer) - - def _map_field(self, field): - warnings.warn( - "Method `_map_field()` has been renamed to `map_field()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.map_field(field) - - def _map_choicefield(self, field): - warnings.warn( - "Method `_map_choicefield()` has been renamed to `map_choicefield()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.map_choicefield(field) - - def _get_pagination_parameters(self, path, method): - warnings.warn( - "Method `_get_pagination_parameters()` has been renamed to `get_pagination_parameters()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.get_pagination_parameters(path, method) - - def _allows_filters(self, path, method): - warnings.warn( - "Method `_allows_filters()` has been renamed to `allows_filters()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 - ) - return self.allows_filters(path, method) - def _get_reference(self, serializer): warnings.warn( "Method `_get_reference()` has been renamed to `get_reference()`. " - "The old name will be removed in DRF v3.14.", - RemovedInDRF314Warning, stacklevel=2 + "The old name will be removed in DRF v3.15.", + RemovedInDRF315Warning, stacklevel=2 ) return self.get_reference(serializer) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 052a766cd8a..083910174bd 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -52,7 +52,7 @@ BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField, - ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, + ListField, ModelField, MultipleChoiceField, ReadOnlyField, RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField, ) from rest_framework.relations import ( # NOQA # isort:skip diff --git a/tests/test_fields.py b/tests/test_fields.py index 56276e6ffc1..11e293107d7 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -679,9 +679,9 @@ def test_disallow_unhashable_collection_types(self): assert exc_info.value.detail == expected -class TestNullBooleanField(TestBooleanField): +class TestNullableBooleanField(TestBooleanField): """ - Valid and invalid values for `NullBooleanField`. + Valid and invalid values for `BooleanField` when `allow_null=True`. """ valid_inputs = { 'true': True, @@ -706,16 +706,6 @@ class TestNullBooleanField(TestBooleanField): field = serializers.BooleanField(allow_null=True) -class TestNullableBooleanField(TestNullBooleanField): - """ - Valid and invalid values for `BooleanField` when `allow_null=True`. - """ - - @property - def field(self): - return serializers.BooleanField(allow_null=True) - - # String types... class TestCharField(FieldValues):