django-rest-framework/community/3.12-announcement/index.html
2024-03-16 19:58:38 +00:00

678 lines
27 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>3.12 Announcement - Django REST framework</title>
<link href="../../img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="canonical" href="https://www.django-rest-framework.org/community/3.12-announcement/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST, 3.12 Announcement">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
<link href="../../css/prettify.css" rel="stylesheet">
<link href="../../css/bootstrap.css" rel="stylesheet">
<link href="../../css/bootstrap-responsive.css" rel="stylesheet">
<link href="../../css/default.css" rel="stylesheet">
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18852272-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#sidebarInclude img {
margin-bottom: 10px;
}
#sidebarInclude a.promo {
color: black;
}
@media (max-width: 767px) {
div.promo {
display: none;
}
}
</style>
</head>
<body onload="prettyPrint()" class="-page">
<div class="wrapper">
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/encode/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small " rel="next" href="../3.11-announcement/">
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small " rel="prev" href="../3.13-announcement/">
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="https://www.django-rest-framework.org/">Django REST framework</a>
<div class="nav-collapse collapse">
<!-- Main navigation -->
<ul class="nav navbar-nav">
<li >
<a href="../..">Home</a>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../tutorial/quickstart/">Quickstart</a>
</li>
<li >
<a href="../../tutorial/1-serialization/">1 - Serialization</a>
</li>
<li >
<a href="../../tutorial/2-requests-and-responses/">2 - Requests and responses</a>
</li>
<li >
<a href="../../tutorial/3-class-based-views/">3 - Class based views</a>
</li>
<li >
<a href="../../tutorial/4-authentication-and-permissions/">4 - Authentication and permissions</a>
</li>
<li >
<a href="../../tutorial/5-relationships-and-hyperlinked-apis/">5 - Relationships and hyperlinked APIs</a>
</li>
<li >
<a href="../../tutorial/6-viewsets-and-routers/">6 - Viewsets and routers</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">API Guide <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../api-guide/requests/">Requests</a>
</li>
<li >
<a href="../../api-guide/responses/">Responses</a>
</li>
<li >
<a href="../../api-guide/views/">Views</a>
</li>
<li >
<a href="../../api-guide/generic-views/">Generic views</a>
</li>
<li >
<a href="../../api-guide/viewsets/">Viewsets</a>
</li>
<li >
<a href="../../api-guide/routers/">Routers</a>
</li>
<li >
<a href="../../api-guide/parsers/">Parsers</a>
</li>
<li >
<a href="../../api-guide/renderers/">Renderers</a>
</li>
<li >
<a href="../../api-guide/serializers/">Serializers</a>
</li>
<li >
<a href="../../api-guide/fields/">Serializer fields</a>
</li>
<li >
<a href="../../api-guide/relations/">Serializer relations</a>
</li>
<li >
<a href="../../api-guide/validators/">Validators</a>
</li>
<li >
<a href="../../api-guide/authentication/">Authentication</a>
</li>
<li >
<a href="../../api-guide/permissions/">Permissions</a>
</li>
<li >
<a href="../../api-guide/caching/">Caching</a>
</li>
<li >
<a href="../../api-guide/throttling/">Throttling</a>
</li>
<li >
<a href="../../api-guide/filtering/">Filtering</a>
</li>
<li >
<a href="../../api-guide/pagination/">Pagination</a>
</li>
<li >
<a href="../../api-guide/versioning/">Versioning</a>
</li>
<li >
<a href="../../api-guide/content-negotiation/">Content negotiation</a>
</li>
<li >
<a href="../../api-guide/metadata/">Metadata</a>
</li>
<li >
<a href="../../api-guide/schemas/">Schemas</a>
</li>
<li >
<a href="../../api-guide/format-suffixes/">Format suffixes</a>
</li>
<li >
<a href="../../api-guide/reverse/">Returning URLs</a>
</li>
<li >
<a href="../../api-guide/exceptions/">Exceptions</a>
</li>
<li >
<a href="../../api-guide/status-codes/">Status codes</a>
</li>
<li >
<a href="../../api-guide/testing/">Testing</a>
</li>
<li >
<a href="../../api-guide/settings/">Settings</a>
</li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Topics <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../../topics/documenting-your-api/">Documenting your API</a>
</li>
<li >
<a href="../../topics/internationalization/">Internationalization</a>
</li>
<li >
<a href="../../topics/ajax-csrf-cors/">AJAX, CSRF & CORS</a>
</li>
<li >
<a href="../../topics/html-and-forms/">HTML & Forms</a>
</li>
<li >
<a href="../../topics/browser-enhancements/">Browser Enhancements</a>
</li>
<li >
<a href="../../topics/browsable-api/">The Browsable API</a>
</li>
<li >
<a href="../../topics/rest-hypermedia-hateoas/">REST, Hypermedia & HATEOAS</a>
</li>
</ul>
</li>
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Community <b class="caret"></b></a>
<ul class="dropdown-menu">
<li >
<a href="../tutorials-and-resources/">Tutorials and Resources</a>
</li>
<li >
<a href="../third-party-packages/">Third Party Packages</a>
</li>
<li >
<a href="../contributing/">Contributing to REST framework</a>
</li>
<li >
<a href="../project-management/">Project management</a>
</li>
<li >
<a href="../release-notes/">Release Notes</a>
</li>
<li >
<a href="../3.15-announcement/">3.15 Announcement</a>
</li>
<li >
<a href="../3.14-announcement/">3.14 Announcement</a>
</li>
<li >
<a href="../3.13-announcement/">3.13 Announcement</a>
</li>
<li class="active" >
<a href="./">3.12 Announcement</a>
</li>
<li >
<a href="../3.11-announcement/">3.11 Announcement</a>
</li>
<li >
<a href="../3.10-announcement/">3.10 Announcement</a>
</li>
<li >
<a href="../3.9-announcement/">3.9 Announcement</a>
</li>
<li >
<a href="../3.8-announcement/">3.8 Announcement</a>
</li>
<li >
<a href="../3.7-announcement/">3.7 Announcement</a>
</li>
<li >
<a href="../3.6-announcement/">3.6 Announcement</a>
</li>
<li >
<a href="../3.5-announcement/">3.5 Announcement</a>
</li>
<li >
<a href="../3.4-announcement/">3.4 Announcement</a>
</li>
<li >
<a href="../3.3-announcement/">3.3 Announcement</a>
</li>
<li >
<a href="../3.2-announcement/">3.2 Announcement</a>
</li>
<li >
<a href="../3.1-announcement/">3.1 Announcement</a>
</li>
<li >
<a href="../3.0-announcement/">3.0 Announcement</a>
</li>
<li >
<a href="../kickstarter-announcement/">Kickstarter Announcement</a>
</li>
<li >
<a href="../mozilla-grant/">Mozilla Grant</a>
</li>
<li >
<a href="../funding/">Funding</a>
</li>
<li >
<a href="../jobs/">Jobs</a>
</li>
</ul>
</li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<div class="body-content">
<div class="container-fluid">
<!-- Search Modal -->
<div id="mkdocs_search_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 id="myModalLabel">Documentation search</h3>
</div>
<div class="modal-body">
<form role="form" autocomplete="off">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search..." id="mkdocs-search-query">
</div>
</form>
<div id="mkdocs-search-results"></div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
<li class="main">
<a href="#django-rest-framework-312">Django REST framework 3.12</a>
</li>
<li>
<a href="#grouping-operations-with-tags">Grouping operations with tags.</a>
</li>
<li>
<a href="#customizing-the-operation-id">Customizing the operation ID.</a>
</li>
<li>
<a href="#support-for-openapi-components">Support for OpenAPI components.</a>
</li>
<li>
<a href="#more-public-api">More Public API</a>
</li>
<li>
<a href="#support-for-jsonfield">Support for JSONField.</a>
</li>
<li>
<a href="#searchfilter-improvements">SearchFilter improvements</a>
</li>
<li>
<a href="#deprecations">Deprecations</a>
</li>
<li>
<a href="#funding">Funding</a>
</li>
<div class="promo">
<hr/>
<div id="sidebarInclude">
</div>
</ul>
</div>
</div>
<div id="main-content" class="span9">
<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>
<h1 id="django-rest-framework-312"><a class="toclink" href="#django-rest-framework-312">Django REST framework 3.12</a></h1>
<p>REST framework 3.12 brings a handful of refinements to the OpenAPI schema
generation, plus support for Django's new database-agnostic <code>JSONField</code>,
and some improvements to the <code>SearchFilter</code> class.</p>
<h2 id="grouping-operations-with-tags"><a class="toclink" href="#grouping-operations-with-tags">Grouping operations with tags.</a></h2>
<p>Open API schemas will now automatically include tags, based on the first element
in the URL path.</p>
<p>For example...</p>
<table>
<thead>
<tr>
<th>Method</th>
<th>Path</th>
<th>Tags</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>GET</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code></td>
<td><code>/users/{id}/</code></td>
<td><code>['users']</code></td>
</tr>
<tr>
<td><code>GET</code>, <code>POST</code></td>
<td><code>/users/</code></td>
<td><code>['users']</code></td>
</tr>
<tr>
<td><code>GET</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code></td>
<td><code>/orders/{id}/</code></td>
<td><code>['orders']</code></td>
</tr>
<tr>
<td><code>GET</code>, <code>POST</code></td>
<td><code>/orders/</code></td>
<td><code>['orders']</code></td>
</tr>
</tbody>
</table>
<p>The tags used for a particular view may also be overridden...</p>
<pre><code class="language-python">class MyOrders(APIView):
schema = AutoSchema(tags=[&quot;users&quot;, &quot;orders&quot;])
...
</code></pre>
<p>See <a href="https://www.django-rest-framework.org/api-guide/schemas/#grouping-operations-with-tags">the schema documentation</a> for more information.</p>
<h2 id="customizing-the-operation-id"><a class="toclink" href="#customizing-the-operation-id">Customizing the operation ID.</a></h2>
<p>REST framework automatically determines operation IDs to use in OpenAPI
schemas. The latest version provides more control for overriding the behaviour
used to generate the operation IDs.</p>
<p>See <a href="https://www.django-rest-framework.org/api-guide/schemas/#operationid">the schema documentation</a> for more information.</p>
<h2 id="support-for-openapi-components"><a class="toclink" href="#support-for-openapi-components">Support for OpenAPI components.</a></h2>
<p>In order to output more graceful OpenAPI schemes, REST framework 3.12 now
defines components in the schema, and then references them inside request
and response objects. This is in contrast with the previous approach, which
fully expanded the request and response bodies for each operation.</p>
<p>The names used for a component default to using the serializer class name, <a href="https://www.django-rest-framework.org/api-guide/schemas/#components">but
may be overridden if needed</a>...</p>
<pre><code class="language-python">class MyOrders(APIView):
schema = AutoSchema(component_name=&quot;OrderDetails&quot;)
</code></pre>
<h2 id="more-public-api"><a class="toclink" href="#more-public-api">More Public API</a></h2>
<p>Many methods on the <code>AutoSchema</code> class have now been promoted to public API,
allowing you to more fully customize the schema generation. The following methods
are now available for overriding...</p>
<ul>
<li><code>get_path_parameters</code></li>
<li><code>get_pagination_parameters</code></li>
<li><code>get_filter_parameters</code></li>
<li><code>get_request_body</code></li>
<li><code>get_responses</code></li>
<li><code>get_serializer</code></li>
<li><code>get_paginator</code></li>
<li><code>map_serializer</code></li>
<li><code>map_field</code></li>
<li><code>map_choice_field</code></li>
<li><code>map_field_validators</code></li>
<li><code>allows_filters</code>.</li>
</ul>
<p>See <a href="https://www.django-rest-framework.org/api-guide/schemas/#per-view-customization">the schema docs</a>
for details on using custom <code>AutoSchema</code> subclasses.</p>
<h2 id="support-for-jsonfield"><a class="toclink" href="#support-for-jsonfield">Support for JSONField.</a></h2>
<p>Django 3.1 deprecated the existing <code>django.contrib.postgres.fields.JSONField</code>
in favour of a new database-agnositic <code>JSONField</code>.</p>
<p>REST framework 3.12 now supports this new model field, and <code>ModelSerializer</code>
classes will correctly map the model field.</p>
<h2 id="searchfilter-improvements"><a class="toclink" href="#searchfilter-improvements">SearchFilter improvements</a></h2>
<p>There are a couple of significant improvements to the <code>SearchFilter</code> class.</p>
<h3 id="nested-searches-against-jsonfield-and-hstorefield"><a class="toclink" href="#nested-searches-against-jsonfield-and-hstorefield">Nested searches against JSONField and HStoreField</a></h3>
<p>The class now supports nested search within <code>JSONField</code> and <code>HStoreField</code>, using
the double underscore notation for traversing which element of the field the
search should apply to.</p>
<pre><code class="language-python">class SitesSearchView(generics.ListAPIView):
&quot;&quot;&quot;
An API view to return a list of archaeological sites, optionally filtered
by a search against the site name or location. (Location searches are
matched against the region and country names.)
&quot;&quot;&quot;
queryset = Sites.objects.all()
serializer_class = SitesSerializer
filter_backends = [filters.SearchFilter]
search_fields = [&quot;site_name&quot;, &quot;location__region&quot;, &quot;location__country&quot;]
</code></pre>
<h3 id="searches-against-annotate-fields"><a class="toclink" href="#searches-against-annotate-fields">Searches against annotate fields</a></h3>
<p>Django allows querysets to create additional virtual fields, using the <code>.annotate</code>
method. We now support searching against annotate fields.</p>
<pre><code class="language-python">class PublisherSearchView(generics.ListAPIView):
&quot;&quot;&quot;
Search for publishers, optionally filtering the search against the average
rating of all their books.
&quot;&quot;&quot;
queryset = Publisher.objects.annotate(avg_rating=Avg(&quot;book__rating&quot;))
serializer_class = PublisherSerializer
filter_backends = [filters.SearchFilter]
search_fields = [&quot;avg_rating&quot;]
</code></pre>
<hr />
<h2 id="deprecations"><a class="toclink" href="#deprecations">Deprecations</a></h2>
<h3 id="serializersnullbooleanfield"><a class="toclink" href="#serializersnullbooleanfield"><code>serializers.NullBooleanField</code></a></h3>
<p><code>serializers.NullBooleanField</code> is now pending deprecation, and will be removed in 3.14.</p>
<p>Instead use <code>serializers.BooleanField</code> field and set <code>allow_null=True</code> which does the same thing.</p>
<hr />
<h2 id="funding"><a class="toclink" href="#funding">Funding</a></h2>
<p>REST framework is a <em>collaboratively funded project</em>. If you use
REST framework commercially we strongly encourage you to invest in its
continued development by <strong><a href="../funding/">signing up for a paid plan</a></strong>.</p>
<p><em>Every single sign-up helps us make REST framework long-term financially sustainable.</em></p>
<ul class="premium-promo promo">
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
<li><a href="https://software.esg-usa.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/esg-new-logo.png)">ESG</a></li>
<li><a href="https://rollbar.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rollbar2.png)">Rollbar</a></li>
<li><a href="https://cadre.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/cadre.png)">Cadre</a></li>
<li><a href="https://hubs.ly/H0f30Lf0" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/kloudless-plus-text.png)">Kloudless</a></li>
<li><a href="https://lightsonsoftware.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/lightson-dark.png)">Lights On Software</a></li>
<li><a href="https://retool.com/?utm_source=djangorest&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/retool-sidebar.png)">Retool</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
<p><em>Many thanks to all our <a href="https://fund.django-rest-framework.org/topics/funding/#our-sponsors">wonderful sponsors</a>, and in particular to our premium backers, <a href="https://getsentry.com/welcome/">Sentry</a>, <a href="https://getstream.io/?utm_source=drf&amp;utm_medium=banner&amp;utm_campaign=drf">Stream</a>, <a href="https://software.esg-usa.com/">ESG</a>, <a href="https://rollbar.com/?utm_source=django&amp;utm_medium=sponsorship&amp;utm_campaign=freetrial">Rollbar</a>, <a href="https://cadre.com">Cadre</a>, <a href="https://hubs.ly/H0f30Lf0">Kloudless</a>, <a href="https://lightsonsoftware.com">Lights On Software</a>, and <a href="https://retool.com/?utm_source=djangorest&amp;utm_medium=sponsorship">Retool</a>.</em></p>
</div> <!--/span-->
</div> <!--/row-->
</div> <!--/.fluid-container-->
</div> <!--/.body content-->
<div id="push"></div>
</div> <!--/.wrapper -->
<footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p>
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script async src="https://fund.django-rest-framework.org/sidebar_include.js"></script>
<script src="../../js/jquery-1.8.1-min.js"></script>
<script src="../../js/prettify-1.0.js"></script>
<script src="../../js/bootstrap-2.1.1-min.js"></script>
<script src="../../js/theme.js"></script>
<script>var base_url = '../..';</script>
<script src="../../search/main.js" defer></script>
<script>
var shiftWindow = function() {
scrollBy(0, -50)
};
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
$('.dropdown-menu').on('click touchstart', function(event) {
event.stopPropagation();
});
// Dynamically force sidenav/dropdown to no higher than browser window
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
$(function() {
$(window).resize(function() {
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
});
});
</script>
</body>
</html>