Skip to content

Commit

Permalink
Version 3.8 Release (#5769)
Browse files Browse the repository at this point in the history
  • Loading branch information
carltongibson authored Apr 3, 2018
1 parent 1befab7 commit fc588f5
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 13 deletions.
2 changes: 1 addition & 1 deletion docs/api-guide/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ See the pagination documentation for further guidance on [setting the pagination

---

**This setting is pending deprecation.**
**This setting has been removed.**

See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style).

Expand Down
97 changes: 97 additions & 0 deletions docs/topics/3.8-announcement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>

# Django REST framework 3.8

The 3.8 release is a maintenance focused release resolving a large number of previously outstanding issues and laying
the foundations for future changes.

---

## Funding

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
**[signing up for a paid&nbsp;plan][funding]**.


*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).*

---

## Breaking Changes

### Altered the behaviour of `read_only` plus `default` on Field.

[#5886][gh5886] `read_only` fields will now **always** be excluded from writable fields.

Previously `read_only` fields when combined with a `default` value would use the `default` for create and update
operations. This was counter-intuitive in some circumstances and led to difficulties supporting dotted `source`
attributes on nullable relations.

In order to maintain the old behaviour you may need to pass the value of `read_only` fields when calling `save()` in
the view:

def perform_create(self, serializer):
serializer.save(owner=self.request.user)

Alternatively you may override `save()` or `create()` or `update()` on the serialiser as appropriate.

---

## Deprecations

### `action` decorator replaces `list_route` and `detail_route`

[#5705][gh5705] `list_route` and `detail_route` have been merge into a single `action` decorator. This improves viewset action introspection, and will allow extra actions to be displayed in the Browsable API in future versions.

Both `list_route` and `detail_route` are now pending deprecation. They will be deprecated in 3.9 and removed entirely
in 3.10.

The new `action` decorator takes a boolean `detail` argument.

* Replace `detail_route` uses with `@action(detail=True)`.
* Replace `list_route` uses with `@action(detail=False)`.


### `exclude_from_schema`

Both `APIView.exclude_from_schema` and the `exclude_from_schema` argument to the `@api_view` decorator are now deprecated. They will be removed entirely in 3.9.

For `APIView` you should instead set a `schema = None` attribute on the view class.

For function based views the `@schema` decorator can be used to exclude the view from the schema, by using `@schema(None)`.

---

## Minor fixes and improvements

There are a large number of minor fixes and improvements in this release. See the [release notes](release-notes.md) page
for a complete listing.


## What's next

We're currently working towards moving to using [OpenAPI][openapi] as our default schema output. We'll also be revisiting our API documentation generation and client libraries.

We're doing some consolidation in order to make this happen. It's planned that 3.9 will drop the `coreapi` and `coreschema` libraries, and instead use `apistar` for the API documentation generation, schema generation, and API client libraries.

[funding]: funding.md
[gh5886]: https://github.com/encode/django-rest-framework/issues/5886
[gh5705]: https://github.com/encode/django-rest-framework/issues/5705
[openapi]: https://www.openapis.org/
149 changes: 148 additions & 1 deletion docs/topics/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,41 @@ You can determine your currently installed version using `pip show`:

### 3.8.0

**Date**: [unreleased][3.8.0-milestone]
**Date**: [3rd April 2018][3.8.0-milestone]


* **Breaking Change**: Alter `read_only` plus `default` behaviour. [#5886][gh5886]

`read_only` fields will now **always** be excluded from writable fields.

Previously `read_only` fields with a `default` value would use the `default` for create and update operations.

In order to maintain the old behaviour you may need to pass the value of `read_only` fields when calling `save()` in
the view:

def perform_create(self, serializer):
serializer.save(owner=self.request.user)

Alternatively you may override `save()` or `create()` or `update()` on the serialiser as appropriate.
* Correct allow_null behaviour when required=False [#5888][gh5888]

Without an explicit `default`, `allow_null` implies a default of `null` for outgoing serialisation. Previously such
fields were being skipped when read-only or otherwise not required.

**Possible backwards compatibility break** if you were relying on such fields being excluded from the outgoing
representation. In order to restore the old behaviour you can override `data` to exclude the field when `None`.

For example:

@property
def data(self):
"""
Drop `maybe_none` field if None.
"""
data = super().data()
if 'maybe_none' in data and data['maybe_none'] is None:
del data['maybe_none']
return data

* Refactor dynamic route generation and improve viewset action introspectibility. [#5705][gh5705]

Expand All @@ -62,6 +96,61 @@ You can determine your currently installed version using `pip show`:
* Deprecated `list_route` & `detail_route` in favor of `action` decorator with `detail` boolean.
* Deprecated dynamic list/detail route variants in favor of `DynamicRoute` with `detail` boolean.
* Refactored the router's dynamic route generation.
* Fix formatting of the 3.7.4 release note [#5704][gh5704]
* Docs: Update DRF Writable Nested Serializers references [#5711][gh5711]
* Docs: Fixed typo in auth URLs example. [#5713][gh5713]
* Improve composite field child errors [#5655][gh5655]
* Disable HTML inputs for dict/list fields [#5702][gh5702]
* Fix typo in HostNameVersioning doc [#5709][gh5709]
* Use rsplit to get module and classname for imports [#5712][gh5712]
* Formalize URLPatternsTestCase [#5703][gh5703]
* Add exception translation test [#5700][gh5700]
* Test staticfiles [#5701][gh5701]
* Add drf-yasg to documentation and schema 3rd party packages [#5720][gh5720]
* Remove unused `compat._resolve_model()` [#5733][gh5733]
* Drop compat workaround for unsupported Python 3.2 [#5734][gh5734]
* Prefer `iter(dict)` over `iter(dict.keys())` [#5736][gh5736]
* Pass `python_requires` argument to setuptools [#5739][gh5739]
* Remove unused links from docs [#5735][gh5735]
* Prefer https protocol for links in docs when available [#5729][gh5729]
* Add HStoreField, postgres fields tests [#5654][gh5654]
* Always fully qualify ValidationError in docs [#5751][gh5751]
* Remove unreachable code from ManualSchema [#5766][gh5766]
* Allowed customising API documentation code samples [#5752][gh5752]
* Updated docs to use `pip show` [#5757][gh5757]
* Load 'static' instead of 'staticfiles' in templates [#5773][gh5773]
* Fixed a typo in `fields` docs [#5783][gh5783]
* Refer to "NamespaceVersioning" instead of "NamespacedVersioning" in the documentation [#5754][gh5754]
* ErrorDetail: add `__eq__`/`__ne__` and `__repr__` [#5787][gh5787]
* Replace `background-attachment: fixed` in docs [#5777][gh5777]
* Make 404 & 403 responses consistent with `exceptions.APIException` output [#5763][gh5763]
* Small fix to API documentation: schemas [#5796][gh5796]
* Fix schema generation for PrimaryKeyRelatedField [#5764][gh5764]
* Represent serializer DictField as an Object in schema [#5765][gh5765]
* Added docs example reimplementing ObtainAuthToken [#5802][gh5802]
* Add schema to the ObtainAuthToken view [#5676][gh5676]
* Fix request formdata handling [#5800][gh5800]
* Fix authtoken views imports [#5818][gh5818]
* Update pytest, isort [#5815][gh5815] [#5817][gh5817] [#5894][gh5894]
* Fixed active timezone handling for non ISO8601 datetimes. [#5833][gh5833]
* Made TemplateHTMLRenderer render IntegerField inputs when value is `0`. [#5834][gh5834]
* Corrected endpoint in tutorial instructions [#5835][gh5835]
* Add Django Rest Framework Role Filters to Third party packages [#5809][gh5809]
* Use single copy of static assets. Update jQuery [#5823][gh5823]
* Changes ternary conditionals to be PEP308 compliant [#5827][gh5827]
* Added links to 'A Todo List API with React' and 'Blog API' tutorials [#5837][gh5837]
* Fix comment typo in ModelSerializer [#5844][gh5844]
* Add admin to installed apps to avoid test failures. [#5870][gh5870]
* Fixed schema for UUIDField in SimpleMetadata. [#5872][gh5872]
* Corrected docs on router include with namespaces. [#5843][gh5843]
* Test using model objects for dotted source default [#5880][gh5880]
* Allow traversing nullable related fields [#5849][gh5849]
* Added: Tutorial: Django REST with React (Django 2.0) [#5891][gh5891]
* Add `LimitOffsetPagination.get_count` to allow method override [#5846][gh5846]
* Don't show hidden fields in metadata [#5854][gh5854]
* Enable OrderingFilter to handle an empty tuple (or list) for the 'ordering' field. [#5899][gh5899]
* Added generic 500 and 400 JSON error handlers. [#5904][gh5904]


## 3.7.x series

Expand Down Expand Up @@ -1778,4 +1867,62 @@ For older release notes, [please see the version 2.x documentation][old-release-
[gh5697]: https://github.com/encode/django-rest-framework/issues/5697

<!-- 3.8.0 -->
[gh5886]: https://github.com/encode/django-rest-framework/issues/5886
[gh5888]: https://github.com/encode/django-rest-framework/issues/5888
[gh5705]: https://github.com/encode/django-rest-framework/issues/5705
[gh5796]: https://github.com/encode/django-rest-framework/issues/5796
[gh5763]: https://github.com/encode/django-rest-framework/issues/5763
[gh5777]: https://github.com/encode/django-rest-framework/issues/5777
[gh5787]: https://github.com/encode/django-rest-framework/issues/5787
[gh5754]: https://github.com/encode/django-rest-framework/issues/5754
[gh5783]: https://github.com/encode/django-rest-framework/issues/5783
[gh5773]: https://github.com/encode/django-rest-framework/issues/5773
[gh5757]: https://github.com/encode/django-rest-framework/issues/5757
[gh5752]: https://github.com/encode/django-rest-framework/issues/5752
[gh5766]: https://github.com/encode/django-rest-framework/issues/5766
[gh5751]: https://github.com/encode/django-rest-framework/issues/5751
[gh5654]: https://github.com/encode/django-rest-framework/issues/5654
[gh5729]: https://github.com/encode/django-rest-framework/issues/5729
[gh5735]: https://github.com/encode/django-rest-framework/issues/5735
[gh5739]: https://github.com/encode/django-rest-framework/issues/5739
[gh5736]: https://github.com/encode/django-rest-framework/issues/5736
[gh5734]: https://github.com/encode/django-rest-framework/issues/5734
[gh5733]: https://github.com/encode/django-rest-framework/issues/5733
[gh5720]: https://github.com/encode/django-rest-framework/issues/5720
[gh5701]: https://github.com/encode/django-rest-framework/issues/5701
[gh5700]: https://github.com/encode/django-rest-framework/issues/5700
[gh5703]: https://github.com/encode/django-rest-framework/issues/5703
[gh5712]: https://github.com/encode/django-rest-framework/issues/5712
[gh5709]: https://github.com/encode/django-rest-framework/issues/5709
[gh5702]: https://github.com/encode/django-rest-framework/issues/5702
[gh5655]: https://github.com/encode/django-rest-framework/issues/5655
[gh5713]: https://github.com/encode/django-rest-framework/issues/5713
[gh5711]: https://github.com/encode/django-rest-framework/issues/5711
[gh5704]: https://github.com/encode/django-rest-framework/issues/5704
[gh5854]: https://github.com/encode/django-rest-framework/issues/5854
[gh5846]: https://github.com/encode/django-rest-framework/issues/5846
[gh5891]: https://github.com/encode/django-rest-framework/issues/5891
[gh5849]: https://github.com/encode/django-rest-framework/issues/5849
[gh5880]: https://github.com/encode/django-rest-framework/issues/5880
[gh5843]: https://github.com/encode/django-rest-framework/issues/5843
[gh5872]: https://github.com/encode/django-rest-framework/issues/5872
[gh5870]: https://github.com/encode/django-rest-framework/issues/5870
[gh5844]: https://github.com/encode/django-rest-framework/issues/5844
[gh5837]: https://github.com/encode/django-rest-framework/issues/5837
[gh5827]: https://github.com/encode/django-rest-framework/issues/5827
[gh5823]: https://github.com/encode/django-rest-framework/issues/5823
[gh5809]: https://github.com/encode/django-rest-framework/issues/5809
[gh5835]: https://github.com/encode/django-rest-framework/issues/5835
[gh5834]: https://github.com/encode/django-rest-framework/issues/5834
[gh5833]: https://github.com/encode/django-rest-framework/issues/5833
[gh5894]: https://github.com/encode/django-rest-framework/issues/5894
[gh5817]: https://github.com/encode/django-rest-framework/issues/5817
[gh5815]: https://github.com/encode/django-rest-framework/issues/5815
[gh5818]: https://github.com/encode/django-rest-framework/issues/5818
[gh5800]: https://github.com/encode/django-rest-framework/issues/5800
[gh5676]: https://github.com/encode/django-rest-framework/issues/5676
[gh5802]: https://github.com/encode/django-rest-framework/issues/5802
[gh5765]: https://github.com/encode/django-rest-framework/issues/5765
[gh5764]: https://github.com/encode/django-rest-framework/issues/5764
[gh5904]: https://github.com/encode/django-rest-framework/issues/5904
[gh5899]: https://github.com/encode/django-rest-framework/issues/5899
4 changes: 2 additions & 2 deletions rest_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
"""

__title__ = 'Django REST framework'
__version__ = '3.7.7'
__version__ = '3.8.0'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2017 Tom Christie'
__copyright__ = 'Copyright 2011-2018 Tom Christie'

# Version synonym
VERSION = __version__
Expand Down
4 changes: 2 additions & 2 deletions rest_framework/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ def handler(self, *args, **kwargs):

if exclude_from_schema:
warnings.warn(
"The `exclude_from_schema` argument to `api_view` is pending deprecation. "
"The `exclude_from_schema` argument to `api_view` is deprecated. "
"Use the `schema` decorator instead, passing `None`.",
PendingDeprecationWarning
DeprecationWarning
)
WrappedAPIView.exclude_from_schema = exclude_from_schema

Expand Down
4 changes: 2 additions & 2 deletions rest_framework/schemas/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,10 @@ def should_include_endpoint(self, path, callback):
return False # Ignore anything except REST framework views.

if hasattr(callback.cls, 'exclude_from_schema'):
fmt = ("The `{}.exclude_from_schema` attribute is pending deprecation. "
fmt = ("The `{}.exclude_from_schema` attribute is deprecated. "
"Set `schema = None` instead.")
msg = fmt.format(callback.cls.__name__)
warnings.warn(msg, PendingDeprecationWarning)
warnings.warn(msg, DeprecationWarning)
if getattr(callback.cls, 'exclude_from_schema', False):
return False

Expand Down
10 changes: 5 additions & 5 deletions tests/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,15 +871,15 @@ def test_should_include_endpoint_excludes_correctly(self):
assert should_include == expected

def test_deprecations(self):
with pytest.warns(PendingDeprecationWarning) as record:
with pytest.warns(DeprecationWarning) as record:
@api_view(["GET"], exclude_from_schema=True)
def view(request):
pass

assert len(record) == 1
assert str(record[0].message) == (
"The `exclude_from_schema` argument to `api_view` is pending "
"deprecation. Use the `schema` decorator instead, passing `None`."
"The `exclude_from_schema` argument to `api_view` is deprecated. "
"Use the `schema` decorator instead, passing `None`."
)

class OldFashionedExcludedView(APIView):
Expand All @@ -893,13 +893,13 @@ def get(self, request, *args, **kwargs):
]

inspector = EndpointEnumerator(patterns)
with pytest.warns(PendingDeprecationWarning) as record:
with pytest.warns(DeprecationWarning) as record:
inspector.get_api_endpoints()

assert len(record) == 1
assert str(record[0].message) == (
"The `OldFashionedExcludedView.exclude_from_schema` attribute is "
"pending deprecation. Set `schema = None` instead."
"deprecated. Set `schema = None` instead."
)


Expand Down

0 comments on commit fc588f5

Please sign in to comment.