Skip to content

Commit f99a87e

Browse files
Merge branch '39/release' of https://github.com/carltongibson/django-rest-framework into carltongibson-39/release
2 parents 28040b3 + 56101ac commit f99a87e

File tree

9 files changed

+208
-72
lines changed

9 files changed

+208
-72
lines changed

docs/community/3.9-announcement.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<style>
2+
.promo li a {
3+
float: left;
4+
width: 130px;
5+
height: 20px;
6+
text-align: center;
7+
margin: 10px 30px;
8+
padding: 150px 0 0 0;
9+
background-position: 0 50%;
10+
background-size: 130px auto;
11+
background-repeat: no-repeat;
12+
font-size: 120%;
13+
color: black;
14+
}
15+
.promo li {
16+
list-style: none;
17+
}
18+
</style>
19+
20+
# Django REST framework 3.9
21+
22+
The 3.9 release gives access to _extra actions_ in the Browsable API, introduces composable permissions and built-in [OpenAPI][openapi] schema support.
23+
24+
---
25+
26+
## Funding
27+
28+
If you use REST framework commercially and would like to see this work continue, we strongly encourage you to invest in its continued development by
29+
**[signing up for a paid&nbsp;plan][funding]**.
30+
31+
32+
TODO: UPDATE SPONSORS.
33+
34+
*We'd like to say thanks in particular our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), and [Rollbar](https://rollbar.com).*
35+
36+
---
37+
38+
## ViewSet _Extra Actions_ available in the Browsable API
39+
40+
Following the introduction of the `action` decorator in v3.8, _extra actions_ defined on a ViewSet are now available
41+
from the Browsable API.
42+
43+
![Extra Actions displayed in the Browsable API](https://user-images.githubusercontent.com/2370209/32976956-1ca9ab7e-cbf1-11e7-981a-a20cb1e83d63.png)
44+
45+
When defined, a dropdown of "Extra Actions", appropriately filtered to detail/non-detail actions, is displayed.
46+
47+
## In-built OpenAPI schema support
48+
49+
TODO
50+
51+
---
52+
53+
## Deprecations
54+
55+
### `DjangoObjectPermissionsFilter` moved to third-party package.
56+
57+
The `DjangoObjectPermissionsFilter` class is pending deprecation, will be deprecated in 3.10 and removed entirely in 3.11.
58+
59+
It has been moved to the third-party [`djangorestframework-guardian`](https://github.com/rpkilby/django-rest-framework-guardian)
60+
package. Please use this instead.
61+
62+
### Router argument/method renamed to use `basename` for consistency.
63+
64+
* The `Router.register` `base_name` argument has been renamed in favor of `basename`.
65+
* The `Router.get_default_base_name` method has been renamed in favor of `Router.get_default_basename`. [#5990][gh5990]
66+
67+
See [#5990][gh5990].
68+
69+
[gh5990]: https://github.com/encode/django-rest-framework/pull/5990
70+
71+
`base_name` and `get_default_base_name()` are pending deprecation. They will be deprecated in 3.10 and removed entirely in 3.11.
72+
73+
### `action` decorator replaces `list_route` and `detail_route`
74+
75+
Both `list_route` and `detail_route` are now deprecated in favour of the single `action` decorator.
76+
They will be removed entirely in 3.10.
77+
78+
The `action` decorator takes a boolean `detail` argument.
79+
80+
* Replace `detail_route` uses with `@action(detail=True)`.
81+
* Replace `list_route` uses with `@action(detail=False)`.
82+
83+
### `exclude_from_schema`
84+
85+
Both `APIView.exclude_from_schema` and the `exclude_from_schema` argument to the `@api_view` have now been removed.
86+
87+
For `APIView` you should instead set a `schema = None` attribute on the view class.
88+
89+
For function based views the `@schema` decorator can be used to exclude the view from the schema, by using `@schema(None)`.
90+
91+
---
92+
93+
## Minor fixes and improvements
94+
95+
There are a large number of minor fixes and improvements in this release. See the [release notes](release-notes.md) page
96+
for a complete listing.
97+
98+
99+
## What's next
100+
101+
102+
TODO...
103+
104+
105+
[funding]: funding.md
106+
[gh5886]: https://github.com/encode/django-rest-framework/issues/5886
107+
[gh5705]: https://github.com/encode/django-rest-framework/issues/5705
108+
[openapi]: https://www.openapis.org/

docs/community/release-notes.md

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,52 @@ You can determine your currently installed version using `pip show`:
4242

4343
### 3.9.0
4444

45-
**Date**: Unreleased
45+
**Date**: [1st October 2018][3.9.0-milestone]
4646

47+
* Improvements to ViewSet extra actions [#5605][gh5605]
48+
* Fix `action` support for ViewSet suffixes [#6081][gh6081]
49+
* Allow `action` docs sections [#6060][gh6060]
4750
* Deprecate the `Router.register` `base_name` argument in favor of `basename`. [#5990][gh5990]
4851
* Deprecate the `Router.get_default_base_name` method in favor of `Router.get_default_basename`. [#5990][gh5990]
52+
* Change `CharField` to disallow null bytes. [#6073][gh6073]
53+
To revert to the old behavior, subclass `CharField` and remove `ProhibitNullCharactersValidator` from the validators.
54+
```python
55+
class NullableCharField(serializers.CharField):
56+
def __init__(self, *args, **kwargs):
57+
super().__init__(*args, **kwargs)
58+
self.validators = [v for v in self.validators if not isinstance(v, ProhibitNullCharactersValidator)]
59+
```
60+
* Add `OpenAPIRenderer` and `generate_schema` management command. [#6229][gh6229]
61+
* Add OpenAPIRenderer by default, and add schema docs. [#6233][gh6233]
62+
* Allow permissions to be composed [#5753][gh5753]
63+
* Allow nullable BooleanField in Django 2.1 [#6183][gh6183]
64+
* Add testing of Python 3.7 support [#6141][gh6141]
65+
* Test using Django 2.1 final release. [#6109][gh6109]
66+
* Added djangorestframework-datatables to third-party packages [#5931][gh5931]
67+
* Change ISO 8601 date format to exclude year/month [#5936][gh5936]
68+
* Update all pypi.python.org URLs to pypi.org [#5942][gh5942]
69+
* Ensure that html forms (multipart form data) respect optional fields [#5927][gh5927]
70+
* Allow hashing of ErrorDetail. [#5932][gh5932]
71+
* Correct schema parsing for JSONField [#5878][gh5878]
72+
* Render descriptions (from help_text) using safe [#5869][gh5869]
73+
* Removed input value from deault_error_message [#5881][gh5881]
74+
* Added min_value/max_value support in DurationField [#5643][gh5643]
75+
* Fixed instance being overwritten in pk-only optimization try/except block [#5747][gh5747]
76+
* Fixed AttributeError from items filter when value is None [#5981][gh5981]
77+
* Fixed Javascript `e.indexOf` is not a function error [#5982][gh5982]
78+
* Fix schemas for extra actions [#5992][gh5992]
79+
* Improved get_error_detail to use error_dict/error_list [#5785][gh5785]
80+
* Imprvied URLs in Admin renderer [#5988][gh5988]
81+
* Add "Community" section to docs, minor cleanup [#5993][gh5993]
82+
* Moved guardian imports out of compat [#6054][gh6054]
4983
* Deprecate the `DjangoObjectPermissionsFilter` class, moved to the `djangorestframework-guardian` package. [#6075][gh6075]
84+
* Drop Django 1.10 support [#5657][gh5657]
85+
* Only catch TypeError/ValueError for object lookups [#6028][gh6028]
86+
* Handle models without .objects manager in ModelSerializer. [#6111][gh6111]
87+
* Improve ModelSerializer.create() error message. [#6112][gh6112]
88+
* Fix CSRF cookie check failure when using session auth with django 1.11.6+ [#6113][gh6113]
89+
* Updated JWT docs. [#6138][gh6138]
90+
* Fix autoescape not getting passed to urlize_quoted_links filter [#6191][gh6191]
5091

5192

5293
## 3.8.x series
@@ -1093,6 +1134,7 @@ For older release notes, [please see the version 2.x documentation][old-release-
10931134
[3.8.0-milestone]: https://github.com/encode/django-rest-framework/milestone/61?closed=1
10941135
[3.8.1-milestone]: https://github.com/encode/django-rest-framework/milestone/67?closed=1
10951136
[3.8.2-milestone]: https://github.com/encode/django-rest-framework/milestone/68?closed=1
1137+
[3.9.0-milestone]: https://github.com/encode/django-rest-framework/milestone/66?closed=1
10961138

10971139
<!-- 3.0.1 -->
10981140
[gh2013]: https://github.com/encode/django-rest-framework/issues/2013
@@ -1974,5 +2016,39 @@ For older release notes, [please see the version 2.x documentation][old-release-
19742016
[gh5920]: https://github.com/encode/django-rest-framework/issues/5920
19752017

19762018
<!-- 3.9.0 -->
2019+
[gh6109]: https://github.com/encode/django-rest-framework/issues/6109
2020+
[gh6141]: https://github.com/encode/django-rest-framework/issues/6141
2021+
[gh6113]: https://github.com/encode/django-rest-framework/issues/6113
2022+
[gh6112]: https://github.com/encode/django-rest-framework/issues/6112
2023+
[gh6111]: https://github.com/encode/django-rest-framework/issues/6111
2024+
[gh6028]: https://github.com/encode/django-rest-framework/issues/6028
2025+
[gh5657]: https://github.com/encode/django-rest-framework/issues/5657
2026+
[gh6054]: https://github.com/encode/django-rest-framework/issues/6054
2027+
[gh5993]: https://github.com/encode/django-rest-framework/issues/5993
19772028
[gh5990]: https://github.com/encode/django-rest-framework/issues/5990
2029+
[gh5988]: https://github.com/encode/django-rest-framework/issues/5988
2030+
[gh5785]: https://github.com/encode/django-rest-framework/issues/5785
2031+
[gh5992]: https://github.com/encode/django-rest-framework/issues/5992
2032+
[gh5605]: https://github.com/encode/django-rest-framework/issues/5605
2033+
[gh5982]: https://github.com/encode/django-rest-framework/issues/5982
2034+
[gh5981]: https://github.com/encode/django-rest-framework/issues/5981
2035+
[gh5747]: https://github.com/encode/django-rest-framework/issues/5747
2036+
[gh5643]: https://github.com/encode/django-rest-framework/issues/5643
2037+
[gh5881]: https://github.com/encode/django-rest-framework/issues/5881
2038+
[gh5869]: https://github.com/encode/django-rest-framework/issues/5869
2039+
[gh5878]: https://github.com/encode/django-rest-framework/issues/5878
2040+
[gh5932]: https://github.com/encode/django-rest-framework/issues/5932
2041+
[gh5927]: https://github.com/encode/django-rest-framework/issues/5927
2042+
[gh5942]: https://github.com/encode/django-rest-framework/issues/5942
2043+
[gh5936]: https://github.com/encode/django-rest-framework/issues/5936
2044+
[gh5931]: https://github.com/encode/django-rest-framework/issues/5931
2045+
[gh6183]: https://github.com/encode/django-rest-framework/issues/6183
19782046
[gh6075]: https://github.com/encode/django-rest-framework/issues/6075
2047+
[gh6138]: https://github.com/encode/django-rest-framework/issues/6138
2048+
[gh6081]: https://github.com/encode/django-rest-framework/issues/6081
2049+
[gh6073]: https://github.com/encode/django-rest-framework/issues/6073
2050+
[gh6191]: https://github.com/encode/django-rest-framework/issues/6191
2051+
[gh6060]: https://github.com/encode/django-rest-framework/issues/6060
2052+
[gh6233]: https://github.com/encode/django-rest-framework/issues/6233
2053+
[gh5753]: https://github.com/encode/django-rest-framework/issues/5753
2054+
[gh6229]: https://github.com/encode/django-rest-framework/issues/6229

rest_framework/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"""
99

1010
__title__ = 'Django REST framework'
11-
__version__ = '3.8.2'
11+
__version__ = '3.9.0'
1212
__author__ = 'Tom Christie'
1313
__license__ = 'BSD 2-Clause'
1414
__copyright__ = 'Copyright 2011-2018 Tom Christie'

rest_framework/decorators.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from rest_framework.views import APIView
1818

1919

20-
def api_view(http_method_names=None, exclude_from_schema=False):
20+
def api_view(http_method_names=None):
2121
"""
2222
Decorator that converts a function-based view into an APIView subclass.
2323
Takes a list of allowed methods for the view as an argument.
@@ -77,15 +77,8 @@ def handler(self, *args, **kwargs):
7777
WrappedAPIView.schema = getattr(func, 'schema',
7878
APIView.schema)
7979

80-
if exclude_from_schema:
81-
warnings.warn(
82-
"The `exclude_from_schema` argument to `api_view` is deprecated. "
83-
"Use the `schema` decorator instead, passing `None`.",
84-
DeprecationWarning
85-
)
86-
WrappedAPIView.exclude_from_schema = exclude_from_schema
87-
8880
return WrappedAPIView.as_view()
81+
8982
return decorator
9083

9184

@@ -230,9 +223,9 @@ def detail_route(methods=None, **kwargs):
230223
Used to mark a method on a ViewSet that should be routed for detail requests.
231224
"""
232225
warnings.warn(
233-
"`detail_route` is pending deprecation and will be removed in 3.10 in favor of "
226+
"`detail_route` is deprecated and will be removed in 3.10 in favor of "
234227
"`action`, which accepts a `detail` bool. Use `@action(detail=True)` instead.",
235-
PendingDeprecationWarning, stacklevel=2
228+
DeprecationWarning, stacklevel=2
236229
)
237230

238231
def decorator(func):
@@ -248,9 +241,9 @@ def list_route(methods=None, **kwargs):
248241
Used to mark a method on a ViewSet that should be routed for list requests.
249242
"""
250243
warnings.warn(
251-
"`list_route` is pending deprecation and will be removed in 3.10 in favor of "
244+
"`list_route` is deprecated and will be removed in 3.10 in favor of "
252245
"`action`, which accepts a `detail` bool. Use `@action(detail=False)` instead.",
253-
PendingDeprecationWarning, stacklevel=2
246+
DeprecationWarning, stacklevel=2
254247
)
255248

256249
def decorator(func):

rest_framework/routers.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,21 @@
4040
class DynamicDetailRoute(object):
4141
def __new__(cls, url, name, initkwargs):
4242
warnings.warn(
43-
"`DynamicDetailRoute` is pending deprecation and will be removed in 3.10 "
43+
"`DynamicDetailRoute` is deprecated and will be removed in 3.10 "
4444
"in favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
4545
"`DynamicRoute(url, name, True, initkwargs)` instead.",
46-
PendingDeprecationWarning, stacklevel=2
46+
DeprecationWarning, stacklevel=2
4747
)
4848
return DynamicRoute(url, name, True, initkwargs)
4949

5050

5151
class DynamicListRoute(object):
5252
def __new__(cls, url, name, initkwargs):
5353
warnings.warn(
54-
"`DynamicListRoute` is pending deprecation and will be removed in 3.10 in "
54+
"`DynamicListRoute` is deprecated and will be removed in 3.10 in "
5555
"favor of `DynamicRoute`, which accepts a `detail` boolean. Use "
5656
"`DynamicRoute(url, name, False, initkwargs)` instead.",
57-
PendingDeprecationWarning, stacklevel=2
57+
DeprecationWarning, stacklevel=2
5858
)
5959
return DynamicRoute(url, name, False, initkwargs)
6060

@@ -77,7 +77,7 @@ def flatten(list_of_lists):
7777

7878
class RenameRouterMethods(RenameMethodsBase):
7979
renamed_methods = (
80-
('get_default_base_name', 'get_default_basename', DeprecationWarning),
80+
('get_default_base_name', 'get_default_basename', PendingDeprecationWarning),
8181
)
8282

8383

@@ -87,8 +87,8 @@ def __init__(self):
8787

8888
def register(self, prefix, viewset, basename=None, base_name=None):
8989
if base_name is not None:
90-
msg = "The `base_name` argument has been deprecated in favor of `basename`."
91-
warnings.warn(msg, DeprecationWarning, 2)
90+
msg = "The `base_name` argument is pending deprecation in favor of `basename`."
91+
warnings.warn(msg, PendingDeprecationWarning, 2)
9292

9393
assert not (basename and base_name), (
9494
"Do not provide both the `basename` and `base_name` arguments.")

rest_framework/schemas/generators.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
See schemas.__init__.py for package overview.
55
"""
66
import re
7-
import warnings
87
from collections import Counter, OrderedDict
98
from importlib import import_module
109

@@ -207,14 +206,6 @@ def should_include_endpoint(self, path, callback):
207206
if not is_api_view(callback):
208207
return False # Ignore anything except REST framework views.
209208

210-
if hasattr(callback.cls, 'exclude_from_schema'):
211-
fmt = ("The `{}.exclude_from_schema` attribute is deprecated. "
212-
"Set `schema = None` instead.")
213-
msg = fmt.format(callback.cls.__name__)
214-
warnings.warn(msg, DeprecationWarning)
215-
if getattr(callback.cls, 'exclude_from_schema', False):
216-
return False
217-
218209
if callback.cls.schema is None:
219210
return False
220211

tests/test_decorators.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,34 +290,34 @@ def test_action():
290290
raise NotImplementedError
291291

292292
def test_detail_route_deprecation(self):
293-
with pytest.warns(PendingDeprecationWarning) as record:
293+
with pytest.warns(DeprecationWarning) as record:
294294
@detail_route()
295295
def view(request):
296296
raise NotImplementedError
297297

298298
assert len(record) == 1
299299
assert str(record[0].message) == (
300-
"`detail_route` is pending deprecation and will be removed in "
300+
"`detail_route` is deprecated and will be removed in "
301301
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
302302
"`@action(detail=True)` instead."
303303
)
304304

305305
def test_list_route_deprecation(self):
306-
with pytest.warns(PendingDeprecationWarning) as record:
306+
with pytest.warns(DeprecationWarning) as record:
307307
@list_route()
308308
def view(request):
309309
raise NotImplementedError
310310

311311
assert len(record) == 1
312312
assert str(record[0].message) == (
313-
"`list_route` is pending deprecation and will be removed in "
313+
"`list_route` is deprecated and will be removed in "
314314
"3.10 in favor of `action`, which accepts a `detail` bool. Use "
315315
"`@action(detail=False)` instead."
316316
)
317317

318318
def test_route_url_name_from_path(self):
319319
# pre-3.8 behavior was to base the `url_name` off of the `url_path`
320-
with pytest.warns(PendingDeprecationWarning):
320+
with pytest.warns(DeprecationWarning):
321321
@list_route(url_path='foo_bar')
322322
def view(request):
323323
raise NotImplementedError

0 commit comments

Comments
 (0)