'
+ return Response(data)
+
+You can use `StaticHTMLRenderer` either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
+
+**.media_type**: `text/html`
+
+**.format**: `'html'`
+
+**.charset**: `utf-8`
+
+See also: `TemplateHTMLRenderer`
+
+## BrowsableAPIRenderer
+
+Renders data into HTML for the Browsable API:
+
+
+
+This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.
+
+**.media_type**: `text/html`
+
+**.format**: `'api'`
+
+**.charset**: `utf-8`
+
+**.template**: `'rest_framework/api.html'`
+
+#### Customizing BrowsableAPIRenderer
+
+By default the response content will be rendered with the highest priority renderer apart from `BrowsableAPIRenderer`. If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the `get_default_renderer()` method. For example:
+
+ class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
+ def get_default_renderer(self, view):
+ return JSONRenderer()
+
+## AdminRenderer
+
+Renders data into HTML for an admin-like display:
+
+
+
+This renderer is suitable for CRUD-style web APIs that should also present a user-friendly interface for managing the data.
+
+Note that views that have nested or list serializers for their input won't work well with the `AdminRenderer`, as the HTML forms are unable to properly support them.
+
+**Note**: The `AdminRenderer` is only able to include links to detail pages when a properly configured `URL_FIELD_NAME` (`url` by default) attribute is present in the data. For `HyperlinkedModelSerializer` this will be the case, but for `ModelSerializer` or plain `Serializer` classes you'll need to make sure to include the field explicitly. For example here we use models `get_absolute_url` method:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ url = serializers.CharField(source='get_absolute_url', read_only=True)
+
+ class Meta:
+ model = Account
+
+
+**.media_type**: `text/html`
+
+**.format**: `'admin'`
+
+**.charset**: `utf-8`
+
+**.template**: `'rest_framework/admin.html'`
+
+## HTMLFormRenderer
+
+Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `
+
+For more information see the [HTML & Forms][html-and-forms] documentation.
+
+**.media_type**: `text/html`
+
+**.format**: `'form'`
+
+**.charset**: `utf-8`
+
+**.template**: `'rest_framework/horizontal/form.html'`
+
+## MultiPartRenderer
+
+This renderer is used for rendering HTML multipart form data. **It is not suitable as a response renderer**, but is instead used for creating test requests, using REST framework's [test client and test request factory][testing].
+
+**.media_type**: `multipart/form-data; boundary=BoUnDaRyStRiNg`
+
+**.format**: `'multipart'`
+
+**.charset**: `utf-8`
+
+---
+
+# Custom renderers
+
+To implement a custom renderer, you should override `BaseRenderer`, set the `.media_type` and `.format` properties, and implement the `.render(self, data, accepted_media_type=None, renderer_context=None)` method.
+
+The method should return a bytestring, which will be used as the body of the HTTP response.
+
+The arguments passed to the `.render()` method are:
+
+### `data`
+
+The request data, as set by the `Response()` instantiation.
+
+### `accepted_media_type=None`
+
+Optional. If provided, this is the accepted media type, as determined by the content negotiation stage.
+
+Depending on the client's `Accept:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"application/json; nested=true"`.
+
+### `renderer_context=None`
+
+Optional. If provided, this is a dictionary of contextual information provided by the view.
+
+By default this will include the following keys: `view`, `request`, `response`, `args`, `kwargs`.
+
+## Example
+
+The following is an example plaintext renderer that will return a response with the `data` parameter as the content of the response.
+
+ from django.utils.encoding import smart_text
+ from rest_framework import renderers
+
+
+ class PlainTextRenderer(renderers.BaseRenderer):
+ media_type = 'text/plain'
+ format = 'txt'
+
+ def render(self, data, accepted_media_type=None, renderer_context=None):
+ return smart_text(data, encoding=self.charset)
+
+## Setting the character set
+
+By default renderer classes are assumed to be using the `UTF-8` encoding. To use a different encoding, set the `charset` attribute on the renderer.
+
+ class PlainTextRenderer(renderers.BaseRenderer):
+ media_type = 'text/plain'
+ format = 'txt'
+ charset = 'iso-8859-1'
+
+ def render(self, data, accepted_media_type=None, renderer_context=None):
+ return data.encode(self.charset)
+
+Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the `Response` class, with the `charset` attribute set on the renderer used to determine the encoding.
+
+If the renderer returns a bytestring representing raw binary content, you should set a charset value of `None`, which will ensure the `Content-Type` header of the response will not have a `charset` value set.
+
+In some cases you may also want to set the `render_style` attribute to `'binary'`. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.
+
+ class JPEGRenderer(renderers.BaseRenderer):
+ media_type = 'image/jpeg'
+ format = 'jpg'
+ charset = None
+ render_style = 'binary'
+
+ def render(self, data, accepted_media_type=None, renderer_context=None):
+ return data
+
+---
+
+# Advanced renderer usage
+
+You can do some pretty flexible things using REST framework's renderers. Some examples...
+
+* Provide either flat or nested representations from the same endpoint, depending on the requested media type.
+* Serve both regular HTML webpages, and JSON based API responses from the same endpoints.
+* Specify multiple types of HTML representation for API clients to use.
+* Underspecify a renderer's media type, such as using `media_type = 'image/*'`, and use the `Accept` header to vary the encoding of the response.
+
+## Varying behavior by media type
+
+In some cases you might want your view to use different serialization styles depending on the accepted media type. If you need to do this you can access `request.accepted_renderer` to determine the negotiated renderer that will be used for the response.
+
+For example:
+
+ @api_view(['GET'])
+ @renderer_classes([TemplateHTMLRenderer, JSONRenderer])
+ def list_users(request):
+ """
+ A view that can return JSON or HTML representations
+ of the users in the system.
+ """
+ queryset = Users.objects.filter(active=True)
+
+ if request.accepted_renderer.format == 'html':
+ # TemplateHTMLRenderer takes a context dict,
+ # and additionally requires a 'template_name'.
+ # It does not require serialization.
+ data = {'users': queryset}
+ return Response(data, template_name='list_users.html')
+
+ # JSONRenderer requires serialized data as normal.
+ serializer = UserSerializer(instance=queryset)
+ data = serializer.data
+ return Response(data)
+
+## Underspecifying the media type
+
+In some cases you might want a renderer to serve a range of media types.
+In this case you can underspecify the media types it should respond to, by using a `media_type` value such as `image/*`, or `*/*`.
+
+If you underspecify the renderer's media type, you should make sure to specify the media type explicitly when you return the response, using the `content_type` attribute. For example:
+
+ return Response(data, content_type='image/png')
+
+## Designing your media types
+
+For the purposes of many Web APIs, simple `JSON` responses with hyperlinked relations may be sufficient. If you want to fully embrace RESTful design and [HATEOAS] you'll need to consider the design and usage of your media types in more detail.
+
+In [the words of Roy Fielding][quote], "A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types.".
+
+For good examples of custom media types, see GitHub's use of a custom [application/vnd.github+json] media type, and Mike Amundsen's IANA approved [application/vnd.collection+json] JSON-based hypermedia.
+
+## HTML error views
+
+Typically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an `Http404` or `PermissionDenied` exception, or a subclass of `APIException`.
+
+If you're using either the `TemplateHTMLRenderer` or the `StaticHTMLRenderer` and an exception is raised, the behavior is slightly different, and mirrors [Django's default handling of error views][django-error-views].
+
+Exceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence.
+
+* Load and render a template named `{status_code}.html`.
+* Load and render a template named `api_exception.html`.
+* Render the HTTP status code and text, for example "404 Not Found".
+
+Templates will render with a `RequestContext` which includes the `status_code` and `details` keys.
+
+**Note**: If `DEBUG=True`, Django's standard traceback error page will be displayed instead of rendering the HTTP status code and text.
+
+---
+
+# Third party packages
+
+The following third party packages are also available.
+
+## YAML
+
+[REST framework YAML][rest-framework-yaml] provides [YAML][yaml] parsing and rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
+
+#### Installation & configuration
+
+Install using pip.
+
+ $ pip install djangorestframework-yaml
+
+Modify your REST framework settings.
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_PARSER_CLASSES': [
+ 'rest_framework_yaml.parsers.YAMLParser',
+ ],
+ 'DEFAULT_RENDERER_CLASSES': [
+ 'rest_framework_yaml.renderers.YAMLRenderer',
+ ],
+ }
+
+## XML
+
+[REST Framework XML][rest-framework-xml] provides a simple informal XML format. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
+
+#### Installation & configuration
+
+Install using pip.
+
+ $ pip install djangorestframework-xml
+
+Modify your REST framework settings.
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_PARSER_CLASSES': [
+ 'rest_framework_xml.parsers.XMLParser',
+ ],
+ 'DEFAULT_RENDERER_CLASSES': [
+ 'rest_framework_xml.renderers.XMLRenderer',
+ ],
+ }
+
+## JSONP
+
+[REST framework JSONP][rest-framework-jsonp] provides JSONP rendering support. It was previously included directly in the REST framework package, and is now instead supported as a third-party package.
+
+---
+
+**Warning**: If you require cross-domain AJAX requests, you should generally be using the more modern approach of [CORS][cors] as an alternative to `JSONP`. See the [CORS documentation][cors-docs] for more details.
+
+The `jsonp` approach is essentially a browser hack, and is [only appropriate for globally readable API endpoints][jsonp-security], where `GET` requests are unauthenticated and do not require any user permissions.
+
+---
+
+#### Installation & configuration
+
+Install using pip.
+
+ $ pip install djangorestframework-jsonp
+
+Modify your REST framework settings.
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_RENDERER_CLASSES': [
+ 'rest_framework_jsonp.renderers.JSONPRenderer',
+ ],
+ }
+
+## MessagePack
+
+[MessagePack][messagepack] is a fast, efficient binary serialization format. [Juan Riaza][juanriaza] maintains the [djangorestframework-msgpack][djangorestframework-msgpack] package which provides MessagePack renderer and parser support for REST framework.
+
+## Microsoft Excel: XLSX (Binary Spreadsheet Endpoints)
+
+XLSX is the world's most popular binary spreadsheet format. [Tim Allen][flipperpa] of [The Wharton School][wharton] maintains [drf-excel][drf-excel], which renders an endpoint as an XLSX spreadsheet using OpenPyXL, and allows the client to download it. Spreadsheets can be styled on a per-view basis.
+
+#### Installation & configuration
+
+Install using pip.
+
+ $ pip install drf-excel
+
+Modify your REST framework settings.
+
+ REST_FRAMEWORK = {
+ ...
+
+ 'DEFAULT_RENDERER_CLASSES': [
+ 'rest_framework.renderers.JSONRenderer',
+ 'rest_framework.renderers.BrowsableAPIRenderer',
+ 'drf_excel.renderers.XLSXRenderer',
+ ],
+ }
+
+To avoid having a file streamed without a filename (which the browser will often default to the filename "download", with no extension), we need to use a mixin to override the `Content-Disposition` header. If no filename is provided, it will default to `export.xlsx`. For example:
+
+ from rest_framework.viewsets import ReadOnlyModelViewSet
+ from drf_excel.mixins import XLSXFileMixin
+ from drf_excel.renderers import XLSXRenderer
+
+ from .models import MyExampleModel
+ from .serializers import MyExampleSerializer
+
+ class MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
+ queryset = MyExampleModel.objects.all()
+ serializer_class = MyExampleSerializer
+ renderer_classes = [XLSXRenderer]
+ filename = 'my_export.xlsx'
+
+## CSV
+
+Comma-separated values are a plain-text tabular data format, that can be easily imported into spreadsheet applications. [Mjumbe Poe][mjumbewu] maintains the [djangorestframework-csv][djangorestframework-csv] package which provides CSV renderer support for REST framework.
+
+## UltraJSON
+
+[UltraJSON][ultrajson] is an optimized C JSON encoder which can give significantly faster JSON rendering. [Adam Mertz][Amertz08] maintains [drf_ujson2][drf_ujson2], a fork of the now unmaintained [drf-ujson-renderer][drf-ujson-renderer], which implements JSON rendering using the UJSON package.
+
+## CamelCase JSON
+
+[djangorestframework-camel-case] provides camel case JSON renderers and parsers for REST framework. This allows serializers to use Python-style underscored field names, but be exposed in the API as Javascript-style camel case field names. It is maintained by [Vitaly Babiy][vbabiy].
+
+## Pandas (CSV, Excel, PNG)
+
+[Django REST Pandas] provides a serializer and renderers that support additional data processing and output via the [Pandas] DataFrame API. Django REST Pandas includes renderers for Pandas-style CSV files, Excel workbooks (both `.xls` and `.xlsx`), and a number of [other formats]. It is maintained by [S. Andrew Sheppard][sheppard] as part of the [wq Project][wq].
+
+## LaTeX
+
+[Rest Framework Latex] provides a renderer that outputs PDFs using Laulatex. It is maintained by [Pebble (S/F Software)][mypebble].
+
+
+[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/#the-rendering-process
+[conneg]: content-negotiation.md
+[html-and-forms]: ../topics/html-and-forms.md
+[browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers
+[testing]: testing.md
+[HATEOAS]: http://timelessrepo.com/haters-gonna-hateoas
+[quote]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
+[application/vnd.github+json]: https://developer.github.com/v3/media/
+[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/
+[django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views
+[rest-framework-jsonp]: https://jpadilla.github.io/django-rest-framework-jsonp/
+[cors]: https://www.w3.org/TR/cors/
+[cors-docs]: https://www.django-rest-framework.org/topics/ajax-csrf-cors/
+[jsonp-security]: https://stackoverflow.com/questions/613962/is-jsonp-safe-to-use
+[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/
+[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/
+[messagepack]: https://msgpack.org/
+[juanriaza]: https://github.com/juanriaza
+[mjumbewu]: https://github.com/mjumbewu
+[flipperpa]: https://github.com/flipperpa
+[wharton]: https://github.com/wharton
+[drf-excel]: https://github.com/wharton/drf-excel
+[vbabiy]: https://github.com/vbabiy
+[rest-framework-yaml]: https://jpadilla.github.io/django-rest-framework-yaml/
+[rest-framework-xml]: https://jpadilla.github.io/django-rest-framework-xml/
+[yaml]: http://www.yaml.org/
+[djangorestframework-msgpack]: https://github.com/juanriaza/django-rest-framework-msgpack
+[djangorestframework-csv]: https://github.com/mjumbewu/django-rest-framework-csv
+[ultrajson]: https://github.com/esnme/ultrajson
+[Amertz08]: https://github.com/Amertz08
+[drf-ujson-renderer]: https://github.com/gizmag/drf-ujson-renderer
+[drf_ujson2]: https://github.com/Amertz08/drf_ujson2
+[djangorestframework-camel-case]: https://github.com/vbabiy/djangorestframework-camel-case
+[Django REST Pandas]: https://github.com/wq/django-rest-pandas
+[Pandas]: https://pandas.pydata.org/
+[other formats]: https://github.com/wq/django-rest-pandas#supported-formats
+[sheppard]: https://github.com/sheppard
+[wq]: https://github.com/wq
+[mypebble]: https://github.com/mypebble
+[Rest Framework Latex]: https://github.com/mypebble/rest-framework-latex
diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md
new file mode 100644
index 000000000..d072ac436
--- /dev/null
+++ b/docs/api-guide/requests.md
@@ -0,0 +1,142 @@
+---
+source:
+ - request.py
+---
+
+# Requests
+
+> If you're doing REST-based web service stuff ... you should ignore request.POST.
+>
+> — Malcom Tredinnick, [Django developers group][cite]
+
+REST framework's `Request` class extends the standard `HttpRequest`, adding support for REST framework's flexible request parsing and request authentication.
+
+---
+
+# Request parsing
+
+REST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.
+
+## .data
+
+`request.data` returns the parsed content of the request body. This is similar to the standard `request.POST` and `request.FILES` attributes except that:
+
+* It includes all parsed content, including *file and non-file* inputs.
+* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
+* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming [JSON data] similarly to how you handle incoming [form data].
+
+For more details see the [parsers documentation].
+
+## .query_params
+
+`request.query_params` is a more correctly named synonym for `request.GET`.
+
+For clarity inside your code, we recommend using `request.query_params` instead of the Django's standard `request.GET`. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just `GET` requests.
+
+## .parsers
+
+The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSER_CLASSES` setting.
+
+You won't typically need to access this property.
+
+---
+
+**Note:** If a client sends malformed content, then accessing `request.data` may raise a `ParseError`. By default REST framework's `APIView` class or `@api_view` decorator will catch the error and return a `400 Bad Request` response.
+
+If a client sends a request with a content-type that cannot be parsed then a `UnsupportedMediaType` exception will be raised, which by default will be caught and return a `415 Unsupported Media Type` response.
+
+---
+
+# Content negotiation
+
+The request exposes some properties that allow you to determine the result of the content negotiation stage. This allows you to implement behavior such as selecting a different serialization schemes for different media types.
+
+## .accepted_renderer
+
+The renderer instance that was selected by the content negotiation stage.
+
+## .accepted_media_type
+
+A string representing the media type that was accepted by the content negotiation stage.
+
+---
+
+# Authentication
+
+REST framework provides flexible, per-request authentication, that gives you the ability to:
+
+* Use different authentication policies for different parts of your API.
+* Support the use of multiple authentication policies.
+* Provide both user and token information associated with the incoming request.
+
+## .user
+
+`request.user` typically returns an instance of `django.contrib.auth.models.User`, although the behavior depends on the authentication policy being used.
+
+If the request is unauthenticated the default value of `request.user` is an instance of `django.contrib.auth.models.AnonymousUser`.
+
+For more details see the [authentication documentation].
+
+## .auth
+
+`request.auth` returns any additional authentication context. The exact behavior of `request.auth` depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against.
+
+If the request is unauthenticated, or if no additional context is present, the default value of `request.auth` is `None`.
+
+For more details see the [authentication documentation].
+
+## .authenticators
+
+The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Authentication` instances, based on the `authentication_classes` set on the view or based on the `DEFAULT_AUTHENTICATORS` setting.
+
+You won't typically need to access this property.
+
+---
+
+**Note:** You may see a `WrappedAttributeError` raised when calling the `.user` or `.auth` properties. These errors originate from an authenticator as a standard `AttributeError`, however it's necessary that they be re-raised as a different exception type in order to prevent them from being suppressed by the outer property access. Python will not recognize that the `AttributeError` originates from the authenticator and will instead assume that the request object does not have a `.user` or `.auth` property. The authenticator will need to be fixed.
+
+---
+
+# Browser enhancements
+
+REST framework supports a few browser enhancements such as browser-based `PUT`, `PATCH` and `DELETE` forms.
+
+## .method
+
+`request.method` returns the **uppercased** string representation of the request's HTTP method.
+
+Browser-based `PUT`, `PATCH` and `DELETE` forms are transparently supported.
+
+For more information see the [browser enhancements documentation].
+
+## .content_type
+
+`request.content_type`, returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided.
+
+You won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior.
+
+If you do need to access the content type of the request you should use the `.content_type` property in preference to using `request.META.get('HTTP_CONTENT_TYPE')`, as it provides transparent support for browser-based non-form content.
+
+For more information see the [browser enhancements documentation].
+
+## .stream
+
+`request.stream` returns a stream representing the content of the request body.
+
+You won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior.
+
+---
+
+# Standard HttpRequest attributes
+
+As REST framework's `Request` extends Django's `HttpRequest`, all the other standard attributes and methods are also available. For example the `request.META` and `request.session` dictionaries are available as normal.
+
+Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition.
+
+
+[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
+[parsers documentation]: parsers.md
+[JSON data]: parsers.md#jsonparser
+[form data]: parsers.md#formparser
+[authentication documentation]: authentication.md
+[browser enhancements documentation]: ../topics/browser-enhancements.md
diff --git a/docs/api-guide/responses.md b/docs/api-guide/responses.md
new file mode 100644
index 000000000..dbdc8ff2c
--- /dev/null
+++ b/docs/api-guide/responses.md
@@ -0,0 +1,98 @@
+---
+source:
+ - response.py
+---
+
+# Responses
+
+> Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process.
+>
+> — [Django documentation][cite]
+
+REST framework supports HTTP content negotiation by providing a `Response` class which allows you to return content that can be rendered into multiple content types, depending on the client request.
+
+The `Response` class subclasses Django's `SimpleTemplateResponse`. `Response` objects are initialised with data, which should consist of native Python primitives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content.
+
+There's no requirement for you to use the `Response` class, you can also return regular `HttpResponse` or `StreamingHttpResponse` objects from your views if required. Using the `Response` class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats.
+
+Unless you want to heavily customize REST framework for some reason, you should always use an `APIView` class or `@api_view` function for views that return `Response` objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.
+
+---
+
+# Creating responses
+
+## Response()
+
+**Signature:** `Response(data, status=None, template_name=None, headers=None, content_type=None)`
+
+Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives.
+
+The renderers used by the `Response` class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the `Response` object.
+
+You can use REST framework's `Serializer` classes to perform this data serialization, or use your own custom serialization.
+
+Arguments:
+
+* `data`: The serialized data for the response.
+* `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes].
+* `template_name`: A template name to use if `HTMLRenderer` is selected.
+* `headers`: A dictionary of HTTP headers to use in the response.
+* `content_type`: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.
+
+---
+
+# Attributes
+
+## .data
+
+The unrendered, serialized data of the response.
+
+## .status_code
+
+The numeric status code of the HTTP response.
+
+## .content
+
+The rendered content of the response. The `.render()` method must have been called before `.content` can be accessed.
+
+## .template_name
+
+The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the response.
+
+## .accepted_renderer
+
+The renderer instance that will be used to render the response.
+
+Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
+
+## .accepted_media_type
+
+The media type that was selected by the content negotiation stage.
+
+Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
+
+## .renderer_context
+
+A dictionary of additional context information that will be passed to the renderer's `.render()` method.
+
+Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
+
+---
+
+# Standard HttpResponse attributes
+
+The `Response` class extends `SimpleTemplateResponse`, and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way:
+
+ response = Response()
+ response['Cache-Control'] = 'no-cache'
+
+## .render()
+
+**Signature:** `.render()`
+
+As with any other `TemplateResponse`, this method is called to render the serialized data of the response into the final response content. When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance.
+
+You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle.
+
+[cite]: https://docs.djangoproject.com/en/stable/ref/template-response/
+[statuscodes]: status-codes.md
diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md
new file mode 100644
index 000000000..82dd16881
--- /dev/null
+++ b/docs/api-guide/reverse.md
@@ -0,0 +1,58 @@
+---
+source:
+ - reverse.py
+---
+
+# Returning URLs
+
+> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.
+>
+> — Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite]
+
+As a rule, it's probably better practice to return absolute URIs from your Web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`.
+
+The advantages of doing so are:
+
+* It's more explicit.
+* It leaves less work for your API clients.
+* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type.
+* It makes it easy to do things like markup HTML representations with hyperlinks.
+
+REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API.
+
+There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink its output for you, which makes browsing the API much easier.
+
+## reverse
+
+**Signature:** `reverse(viewname, *args, **kwargs)`
+
+Has the same behavior as [`django.urls.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port.
+
+You should **include the request as a keyword argument** to the function, for example:
+
+ from rest_framework.reverse import reverse
+ from rest_framework.views import APIView
+ from django.utils.timezone import now
+
+ class APIRootView(APIView):
+ def get(self, request):
+ year = now().year
+ data = {
+ ...
+ 'year-summary-url': reverse('year-summary', args=[year], request=request)
+ }
+ return Response(data)
+
+## reverse_lazy
+
+**Signature:** `reverse_lazy(viewname, *args, **kwargs)`
+
+Has the same behavior as [`django.urls.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port.
+
+As with the `reverse` function, you should **include the request as a keyword argument** to the function, for example:
+
+ api_root = reverse_lazy('api-root', request=request)
+
+[cite]: https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5
+[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse
+[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
new file mode 100644
index 000000000..91ef0b96e
--- /dev/null
+++ b/docs/api-guide/routers.md
@@ -0,0 +1,355 @@
+---
+source:
+ - routers.py
+---
+
+# Routers
+
+> Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.
+>
+> — [Ruby on Rails Documentation][cite]
+
+Some Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.
+
+REST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.
+
+## Usage
+
+Here's an example of a simple URL conf, that uses `SimpleRouter`.
+
+ from rest_framework import routers
+
+ router = routers.SimpleRouter()
+ router.register(r'users', UserViewSet)
+ router.register(r'accounts', AccountViewSet)
+ urlpatterns = router.urls
+
+There are two mandatory arguments to the `register()` method:
+
+* `prefix` - The URL prefix to use for this set of routes.
+* `viewset` - The viewset class.
+
+Optionally, you may also specify an additional argument:
+
+* `basename` - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the `queryset` attribute of the viewset, if it has one. Note that if the viewset does not include a `queryset` attribute then you must set `basename` when registering the viewset.
+
+The example above would generate the following URL patterns:
+
+* URL pattern: `^users/$` Name: `'user-list'`
+* URL pattern: `^users/{pk}/$` Name: `'user-detail'`
+* URL pattern: `^accounts/$` Name: `'account-list'`
+* URL pattern: `^accounts/{pk}/$` Name: `'account-detail'`
+
+---
+
+**Note**: The `basename` argument is used to specify the initial part of the view name pattern. In the example above, that's the `user` or `account` part.
+
+Typically you won't *need* to specify the `basename` argument, but if you have a viewset where you've defined a custom `get_queryset` method, then the viewset may not have a `.queryset` attribute set. If you try to register that viewset you'll see an error like this:
+
+ 'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.
+
+This means you'll need to explicitly set the `basename` argument when registering the viewset, as it could not be automatically determined from the model name.
+
+---
+
+### Using `include` with routers
+
+The `.urls` attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.
+
+For example, you can append `router.urls` to a list of existing views...
+
+ router = routers.SimpleRouter()
+ router.register(r'users', UserViewSet)
+ router.register(r'accounts', AccountViewSet)
+
+ urlpatterns = [
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
+ ]
+
+ urlpatterns += router.urls
+
+Alternatively you can use Django's `include` function, like so...
+
+ urlpatterns = [
+ path('forgot-password', ForgotPasswordFormView.as_view()),
+ path('', include(router.urls)),
+ ]
+
+You may use `include` with an application namespace:
+
+ urlpatterns = [
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
+ path('api/', include((router.urls, 'app_name'))),
+ ]
+
+Or both an application and instance namespace:
+
+ urlpatterns = [
+ path('forgot-password/', ForgotPasswordFormView.as_view()),
+ path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
+ ]
+
+See Django's [URL namespaces docs][url-namespace-docs] and the [`include` API reference][include-api-reference] for more details.
+
+---
+
+**Note**: If using namespacing with hyperlinked serializers you'll also need to ensure that any `view_name` parameters
+on the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as
+`view_name='app_name:user-detail'` for serializer fields hyperlinked to the user detail view.
+
+The automatic `view_name` generation uses a pattern like `%(model_name)-detail`. Unless your models names actually clash
+you may be better off **not** namespacing your Django REST Framework views when using hyperlinked serializers.
+
+---
+
+### Routing for extra actions
+
+A viewset may [mark extra actions for routing][route-decorators] by decorating a method with the `@action` decorator. These extra actions will be included in the generated routes. For example, given the `set_password` method on the `UserViewSet` class:
+
+ from myapp.permissions import IsAdminOrIsSelf
+ from rest_framework.decorators import action
+
+ class UserViewSet(ModelViewSet):
+ ...
+
+ @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
+ def set_password(self, request, pk=None):
+ ...
+
+The following route would be generated:
+
+* URL pattern: `^users/{pk}/set_password/$`
+* URL name: `'user-set-password'`
+
+By default, the URL pattern is based on the method name, and the URL name is the combination of the `ViewSet.basename` and the hyphenated method name.
+If you don't want to use the defaults for either of these values, you can instead provide the `url_path` and `url_name` arguments to the `@action` decorator.
+
+For example, if you want to change the URL for our custom action to `^users/{pk}/change-password/$`, you could write:
+
+ from myapp.permissions import IsAdminOrIsSelf
+ from rest_framework.decorators import action
+
+ class UserViewSet(ModelViewSet):
+ ...
+
+ @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],
+ url_path='change-password', url_name='change_password')
+ def set_password(self, request, pk=None):
+ ...
+
+The above example would now generate the following URL pattern:
+
+* URL path: `^users/{pk}/change-password/$`
+* URL name: `'user-change_password'`
+
+# API Guide
+
+## SimpleRouter
+
+This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@action` decorator.
+
+
+
URL Style
HTTP Method
Action
URL Name
+
{prefix}/
GET
list
{basename}-list
+
POST
create
+
{prefix}/{url_path}/
GET, or as specified by `methods` argument
`@action(detail=False)` decorated method
{basename}-{url_name}
+
{prefix}/{lookup}/
GET
retrieve
{basename}-detail
+
PUT
update
+
PATCH
partial_update
+
DELETE
destroy
+
{prefix}/{lookup}/{url_path}/
GET, or as specified by `methods` argument
`@action(detail=True)` decorated method
{basename}-{url_name}
+
+
+By default the URLs created by `SimpleRouter` are appended with a trailing slash.
+This behavior can be modified by setting the `trailing_slash` argument to `False` when instantiating the router. For example:
+
+ router = SimpleRouter(trailing_slash=False)
+
+Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.
+
+By default the URLs created by `SimpleRouter` use regular expressions. This behavior can be modified by setting the `use_regex_path` argument to `False` when instantiating the router, in this case [path converters][path-converters-topic-reference] are used. For example:
+
+ router = SimpleRouter(use_regex_path=False)
+
+**Note**: `use_regex_path=False` only works with Django 2.x or above, since this feature was introduced in 2.0.0. See [release note][simplified-routing-release-note]
+
+
+The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the `lookup_value_regex` attribute on the viewset or `lookup_value_converter` if using path converters. For example, you can limit the lookup to valid UUIDs:
+
+ class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
+ lookup_field = 'my_model_id'
+ lookup_value_regex = '[0-9a-f]{32}'
+
+ class MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
+ lookup_field = 'my_model_uuid'
+ lookup_value_converter = 'uuid'
+
+## DefaultRouter
+
+This router is similar to `SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional `.json` style format suffixes.
+
+
+
URL Style
HTTP Method
Action
URL Name
+
[.format]
GET
automatically generated root view
api-root
+
{prefix}/[.format]
GET
list
{basename}-list
+
POST
create
+
{prefix}/{url_path}/[.format]
GET, or as specified by `methods` argument
`@action(detail=False)` decorated method
{basename}-{url_name}
+
{prefix}/{lookup}/[.format]
GET
retrieve
{basename}-detail
+
PUT
update
+
PATCH
partial_update
+
DELETE
destroy
+
{prefix}/{lookup}/{url_path}/[.format]
GET, or as specified by `methods` argument
`@action(detail=True)` decorated method
{basename}-{url_name}
+
+
+As with `SimpleRouter` the trailing slashes on the URL routes can be removed by setting the `trailing_slash` argument to `False` when instantiating the router.
+
+ router = DefaultRouter(trailing_slash=False)
+
+# Custom Routers
+
+Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.
+
+The simplest way to implement a custom router is to subclass one of the existing router classes. The `.routes` attribute is used to template the URL patterns that will be mapped to each viewset. The `.routes` attribute is a list of `Route` named tuples.
+
+The arguments to the `Route` named tuple are:
+
+**url**: A string representing the URL to be routed. May include the following format strings:
+
+* `{prefix}` - The URL prefix to use for this set of routes.
+* `{lookup}` - The lookup field used to match against a single instance.
+* `{trailing_slash}` - Either a '/' or an empty string, depending on the `trailing_slash` argument.
+
+**mapping**: A mapping of HTTP method names to the view methods
+
+**name**: The name of the URL as used in `reverse` calls. May include the following format string:
+
+* `{basename}` - The base to use for the URL names that are created.
+
+**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `detail`, `basename`, and `suffix` arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.
+
+## Customizing dynamic routes
+
+You can also customize how the `@action` decorator is routed. Include the `DynamicRoute` named tuple in the `.routes` list, setting the `detail` argument as appropriate for the list-based and detail-based routes. In addition to `detail`, the arguments to `DynamicRoute` are:
+
+**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{url_path}` format string.
+
+**name**: The name of the URL as used in `reverse` calls. May include the following format strings:
+
+* `{basename}` - The base to use for the URL names that are created.
+* `{url_name}` - The `url_name` provided to the `@action`.
+
+**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view.
+
+## Example
+
+The following example will only route to the `list` and `retrieve` actions, and does not use the trailing slash convention.
+
+ from rest_framework.routers import Route, DynamicRoute, SimpleRouter
+
+ class CustomReadOnlyRouter(SimpleRouter):
+ """
+ A router for read-only APIs, which doesn't use trailing slashes.
+ """
+ routes = [
+ Route(
+ url=r'^{prefix}$',
+ mapping={'get': 'list'},
+ name='{basename}-list',
+ detail=False,
+ initkwargs={'suffix': 'List'}
+ ),
+ Route(
+ url=r'^{prefix}/{lookup}$',
+ mapping={'get': 'retrieve'},
+ name='{basename}-detail',
+ detail=True,
+ initkwargs={'suffix': 'Detail'}
+ ),
+ DynamicRoute(
+ url=r'^{prefix}/{lookup}/{url_path}$',
+ name='{basename}-{url_name}',
+ detail=True,
+ initkwargs={}
+ )
+ ]
+
+Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a simple viewset.
+
+`views.py`:
+
+ class UserViewSet(viewsets.ReadOnlyModelViewSet):
+ """
+ A viewset that provides the standard actions
+ """
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+ lookup_field = 'username'
+
+ @action(detail=True)
+ def group_names(self, request, pk=None):
+ """
+ Returns a list of all the group names that the given
+ user belongs to.
+ """
+ user = self.get_object()
+ groups = user.groups.all()
+ return Response([group.name for group in groups])
+
+`urls.py`:
+
+ router = CustomReadOnlyRouter()
+ router.register('users', UserViewSet)
+ urlpatterns = router.urls
+
+The following mappings would be generated...
+
+
+
URL
HTTP Method
Action
URL Name
+
/users
GET
list
user-list
+
/users/{username}
GET
retrieve
user-detail
+
/users/{username}/group_names
GET
group_names
user-group-names
+
+
+For another example of setting the `.routes` attribute, see the source code for the `SimpleRouter` class.
+
+## Advanced custom routers
+
+If you want to provide totally custom behavior, you can override `BaseRouter` and override the `get_urls(self)` method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the `self.registry` attribute.
+
+You may also want to override the `get_default_basename(self, viewset)` method, or else always explicitly set the `basename` argument when registering your viewsets with the router.
+
+# Third Party Packages
+
+The following third party packages are also available.
+
+## DRF Nested Routers
+
+The [drf-nested-routers package][drf-nested-routers] provides routers and relationship fields for working with nested resources.
+
+## ModelRouter (wq.db.rest)
+
+The [wq.db package][wq.db] provides an advanced [ModelRouter][wq.db-router] class (and singleton instance) that extends `DefaultRouter` with a `register_model()` API. Much like Django's `admin.site.register`, the only required argument to `rest.router.register_model` is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.
+
+ from wq.db import rest
+ from myapp.models import MyModel
+
+ rest.router.register_model(MyModel)
+
+## DRF-extensions
+
+The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
+
+[cite]: https://guides.rubyonrails.org/routing.html
+[route-decorators]: viewsets.md#marking-extra-actions-for-routing
+[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
+[wq.db]: https://wq.io/wq.db
+[wq.db-router]: https://wq.io/docs/router
+[drf-extensions]: https://chibisov.github.io/drf-extensions/docs/
+[drf-extensions-routers]: https://chibisov.github.io/drf-extensions/docs/#routers
+[drf-extensions-nested-viewsets]: https://chibisov.github.io/drf-extensions/docs/#nested-routes
+[drf-extensions-collection-level-controllers]: https://chibisov.github.io/drf-extensions/docs/#collection-level-controllers
+[drf-extensions-customizable-endpoint-names]: https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
+[url-namespace-docs]: https://docs.djangoproject.com/en/4.0/topics/http/urls/#url-namespaces
+[include-api-reference]: https://docs.djangoproject.com/en/4.0/ref/urls/#include
+[simplified-routing-release-note]: https://docs.djangoproject.com/en/2.0/releases/2.0/#simplified-url-routing-syntax
+[path-converters-topic-reference]: https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters
diff --git a/docs/api-guide/schemas.md b/docs/api-guide/schemas.md
new file mode 100644
index 000000000..3f0b15530
--- /dev/null
+++ b/docs/api-guide/schemas.md
@@ -0,0 +1,458 @@
+---
+source:
+ - schemas
+---
+
+# Schema
+
+> A machine-readable [schema] describes what resources are available via the API, what their URLs are, how they are represented and what operations they support.
+>
+> — Heroku, [JSON Schema for the Heroku Platform API][cite]
+
+---
+
+**Deprecation notice:**
+
+REST framework's built-in support for generating OpenAPI schemas is
+**deprecated** in favor of 3rd party packages that can provide this
+functionality instead. The built-in support will be moved into a separate
+package and then subsequently retired over the next releases.
+
+As a full-fledged replacement, we recommend the [drf-spectacular] package.
+It has extensive support for generating OpenAPI 3 schemas from
+REST framework APIs, with both automatic and customisable options available.
+For further information please refer to
+[Documenting your API](../topics/documenting-your-api.md#drf-spectacular).
+
+---
+
+API schemas are a useful tool that allow for a range of use cases, including
+generating reference documentation, or driving dynamic client libraries that
+can interact with your API.
+
+Django REST Framework provides support for automatic generation of
+[OpenAPI][openapi] schemas.
+
+## Overview
+
+Schema generation has several moving parts. It's worth having an overview:
+
+* `SchemaGenerator` is a top-level class that is responsible for walking your
+ configured URL patterns, finding `APIView` subclasses, enquiring for their
+ schema representation, and compiling the final schema object.
+* `AutoSchema` encapsulates all the details necessary for per-view schema
+ introspection. Is attached to each view via the `schema` attribute. You
+ subclass `AutoSchema` in order to customize your schema.
+* The `generateschema` management command allows you to generate a static schema
+ offline.
+* Alternatively, you can route `SchemaView` to dynamically generate and serve
+ your schema.
+* `settings.DEFAULT_SCHEMA_CLASS` allows you to specify an `AutoSchema`
+ subclass to serve as your project's default.
+
+The following sections explain more.
+
+## Generating an OpenAPI Schema
+
+### Install dependencies
+
+ pip install pyyaml uritemplate
+
+* `pyyaml` is used to generate schema into YAML-based OpenAPI format.
+* `uritemplate` is used internally to get parameters in path.
+
+### Generating a static schema with the `generateschema` management command
+
+If your schema is static, you can use the `generateschema` management command:
+
+```bash
+./manage.py generateschema --file openapi-schema.yml
+```
+
+Once you've generated a schema in this way you can annotate it with any
+additional information that cannot be automatically inferred by the schema
+generator.
+
+You might want to check your API schema into version control and update it
+with each new release, or serve the API schema from your site's static media.
+
+### Generating a dynamic schema with `SchemaView`
+
+If you require a dynamic schema, because foreign key choices depend on database
+values, for example, you can route a `SchemaView` that will generate and serve
+your schema on demand.
+
+To route a `SchemaView`, use the `get_schema_view()` helper.
+
+In `urls.py`:
+
+```python
+from rest_framework.schemas import get_schema_view
+
+urlpatterns = [
+ # ...
+ # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.
+ # * `title` and `description` parameters are passed to `SchemaGenerator`.
+ # * Provide view name for use with `reverse()`.
+ path('openapi', get_schema_view(
+ title="Your Project",
+ description="API for all things …",
+ version="1.0.0"
+ ), name='openapi-schema'),
+ # ...
+]
+```
+
+#### `get_schema_view()`
+
+The `get_schema_view()` helper takes the following keyword arguments:
+
+* `title`: May be used to provide a descriptive title for the schema definition.
+* `description`: Longer descriptive text.
+* `version`: The version of the API.
+* `url`: May be used to pass a canonical base URL for the schema.
+
+ schema_view = get_schema_view(
+ title='Server Monitoring API',
+ url='https://www.example.org/api/'
+ )
+
+* `urlconf`: A string representing the import path to the URL conf that you want
+ to generate an API schema for. This defaults to the value of Django's
+ `ROOT_URLCONF` setting.
+
+ schema_view = get_schema_view(
+ title='Server Monitoring API',
+ url='https://www.example.org/api/',
+ urlconf='myproject.urls'
+ )
+
+* `patterns`: List of url patterns to limit the schema introspection to. If you
+ only want the `myproject.api` urls to be exposed in the schema:
+
+ schema_url_patterns = [
+ path('api/', include('myproject.api.urls')),
+ ]
+
+ schema_view = get_schema_view(
+ title='Server Monitoring API',
+ url='https://www.example.org/api/',
+ patterns=schema_url_patterns,
+ )
+* `public`: May be used to specify if schema should bypass views permissions. Default to False
+
+* `generator_class`: May be used to specify a `SchemaGenerator` subclass to be
+ passed to the `SchemaView`.
+* `authentication_classes`: May be used to specify the list of authentication
+ classes that will apply to the schema endpoint. Defaults to
+ `settings.DEFAULT_AUTHENTICATION_CLASSES`
+* `permission_classes`: May be used to specify the list of permission classes
+ that will apply to the schema endpoint. Defaults to
+ `settings.DEFAULT_PERMISSION_CLASSES`.
+* `renderer_classes`: May be used to pass the set of renderer classes that can
+ be used to render the API root endpoint.
+
+
+## SchemaGenerator
+
+**Schema-level customization**
+
+```python
+from rest_framework.schemas.openapi import SchemaGenerator
+```
+
+`SchemaGenerator` is a class that walks a list of routed URL patterns, requests
+the schema for each view and collates the resulting OpenAPI schema.
+
+Typically you won't need to instantiate `SchemaGenerator` yourself, but you can
+do so like so:
+
+ generator = SchemaGenerator(title='Stock Prices API')
+
+Arguments:
+
+* `title` **required**: The name of the API.
+* `description`: Longer descriptive text.
+* `version`: The version of the API. Defaults to `0.1.0`.
+* `url`: The root URL of the API schema. This option is not required unless the schema is included under path prefix.
+* `patterns`: A list of URLs to inspect when generating the schema. Defaults to the project's URL conf.
+* `urlconf`: A URL conf module name to use when generating the schema. Defaults to `settings.ROOT_URLCONF`.
+
+In order to customize the top-level schema, subclass
+`rest_framework.schemas.openapi.SchemaGenerator` and provide your subclass
+as an argument to the `generateschema` command or `get_schema_view()` helper
+function.
+
+### get_schema(self, request=None, public=False)
+
+Returns a dictionary that represents the OpenAPI schema:
+
+ generator = SchemaGenerator(title='Stock Prices API')
+ schema = generator.get_schema()
+
+The `request` argument is optional, and may be used if you want to apply
+per-user permissions to the resulting schema generation.
+
+This is a good point to override if you want to customize the generated
+dictionary For example you might wish to add terms of service to the [top-level
+`info` object][info-object]:
+
+```
+class TOSSchemaGenerator(SchemaGenerator):
+ def get_schema(self, *args, **kwargs):
+ schema = super().get_schema(*args, **kwargs)
+ schema["info"]["termsOfService"] = "https://example.com/tos.html"
+ return schema
+```
+
+## AutoSchema
+
+**Per-View Customization**
+
+```python
+from rest_framework.schemas.openapi import AutoSchema
+```
+
+By default, view introspection is performed by an `AutoSchema` instance
+accessible via the `schema` attribute on `APIView`.
+
+ auto_schema = some_view.schema
+
+`AutoSchema` provides the OpenAPI elements needed for each view, request method
+and path:
+
+* A list of [OpenAPI components][openapi-components]. In DRF terms these are
+ mappings of serializers that describe request and response bodies.
+* The appropriate [OpenAPI operation object][openapi-operation] that describes
+ the endpoint, including path and query parameters for pagination, filtering,
+ and so on.
+
+```python
+components = auto_schema.get_components(...)
+operation = auto_schema.get_operation(...)
+```
+
+In compiling the schema, `SchemaGenerator` calls `get_components()` and
+`get_operation()` for each view, allowed method, and path.
+
+----
+
+**Note**: The automatic introspection of components, and many operation
+parameters relies on the relevant attributes and methods of
+`GenericAPIView`: `get_serializer()`, `pagination_class`, `filter_backends`,
+etc. For basic `APIView` subclasses, default introspection is essentially limited to
+the URL kwarg path parameters for this reason.
+
+----
+
+`AutoSchema` encapsulates the view introspection needed for schema generation.
+Because of this all the schema generation logic is kept in a single place,
+rather than being spread around the already extensive view, serializer and
+field APIs.
+
+Keeping with this pattern, try not to let schema logic leak into your own
+views, serializers, or fields when customizing the schema generation. You might
+be tempted to do something like this:
+
+```python
+class CustomSchema(AutoSchema):
+ """
+ AutoSchema subclass using schema_extra_info on the view.
+ """
+ ...
+
+class CustomView(APIView):
+ schema = CustomSchema()
+ schema_extra_info = ... some extra info ...
+```
+
+Here, the `AutoSchema` subclass goes looking for `schema_extra_info` on the
+view. This is _OK_ (it doesn't actually hurt) but it means you'll end up with
+your schema logic spread out in a number of different places.
+
+Instead try to subclass `AutoSchema` such that the `extra_info` doesn't leak
+out into the view:
+
+```python
+class BaseSchema(AutoSchema):
+ """
+ AutoSchema subclass that knows how to use extra_info.
+ """
+ ...
+
+class CustomSchema(BaseSchema):
+ extra_info = ... some extra info ...
+
+class CustomView(APIView):
+ schema = CustomSchema()
+```
+
+This style is slightly more verbose but maintains the encapsulation of the
+schema related code. It's more _cohesive_ in the _parlance_. It'll keep the
+rest of your API code more tidy.
+
+If an option applies to many view classes, rather than creating a specific
+subclass per-view, you may find it more convenient to allow specifying the
+option as an `__init__()` kwarg to your base `AutoSchema` subclass:
+
+```python
+class CustomSchema(BaseSchema):
+ def __init__(self, **kwargs):
+ # store extra_info for later
+ self.extra_info = kwargs.pop("extra_info")
+ super().__init__(**kwargs)
+
+class CustomView(APIView):
+ schema = CustomSchema(
+ extra_info=... some extra info ...
+ )
+```
+
+This saves you having to create a custom subclass per-view for a commonly used option.
+
+Not all `AutoSchema` methods expose related `__init__()` kwargs, but those for
+the more commonly needed options do.
+
+### `AutoSchema` methods
+
+#### `get_components()`
+
+Generates the OpenAPI components that describe request and response bodies,
+deriving their properties from the serializer.
+
+Returns a dictionary mapping the component name to the generated
+representation. By default this has just a single pair but you may override
+`get_components()` to return multiple pairs if your view uses multiple
+serializers.
+
+#### `get_component_name()`
+
+Computes the component's name from the serializer.
+
+You may see warnings if your API has duplicate component names. If so you can override `get_component_name()` or pass the `component_name` `__init__()` kwarg (see below) to provide different names.
+
+#### `get_reference()`
+
+Returns a reference to the serializer component. This may be useful if you override `get_schema()`.
+
+
+#### `map_serializer()`
+
+Maps serializers to their OpenAPI representations.
+
+Most serializers should conform to the standard OpenAPI `object` type, but you may
+wish to override `map_serializer()` in order to customize this or other
+serializer-level fields.
+
+#### `map_field()`
+
+Maps individual serializer fields to their schema representation. The base implementation
+will handle the default fields that Django REST Framework provides.
+
+For `SerializerMethodField` instances, for which the schema is unknown, or custom field subclasses you should override `map_field()` to generate the correct schema:
+
+```python
+class CustomSchema(AutoSchema):
+ """Extension of ``AutoSchema`` to add support for custom field schemas."""
+
+ def map_field(self, field):
+ # Handle SerializerMethodFields or custom fields here...
+ # ...
+ return super().map_field(field)
+```
+
+Authors of third-party packages should aim to provide an `AutoSchema` subclass,
+and a mixin, overriding `map_field()` so that users can easily generate schemas
+for their custom fields.
+
+#### `get_tags()`
+
+OpenAPI groups operations by tags. By default tags taken from the first path
+segment of the routed URL. For example, a URL like `/users/{id}/` will generate
+the tag `users`.
+
+You can pass an `__init__()` kwarg to manually specify tags (see below), or
+override `get_tags()` to provide custom logic.
+
+#### `get_operation()`
+
+Returns the [OpenAPI operation object][openapi-operation] that describes the
+endpoint, including path and query parameters for pagination, filtering, and so
+on.
+
+Together with `get_components()`, this is the main entry point to the view
+introspection.
+
+#### `get_operation_id()`
+
+There must be a unique [operationid](openapi-operationid) for each operation.
+By default the `operationId` is deduced from the model name, serializer name or
+view name. The operationId looks like "listItems", "retrieveItem",
+"updateItem", etc. The `operationId` is camelCase by convention.
+
+#### `get_operation_id_base()`
+
+If you have several views with the same model name, you may see duplicate
+operationIds.
+
+In order to work around this, you can override `get_operation_id_base()` to
+provide a different base for name part of the ID.
+
+#### `get_serializer()`
+
+If the view has implemented `get_serializer()`, returns the result.
+
+#### `get_request_serializer()`
+
+By default returns `get_serializer()` but can be overridden to
+differentiate between request and response objects.
+
+#### `get_response_serializer()`
+
+By default returns `get_serializer()` but can be overridden to
+differentiate between request and response objects.
+
+### `AutoSchema.__init__()` kwargs
+
+`AutoSchema` provides a number of `__init__()` kwargs that can be used for
+common customizations, if the default generated values are not appropriate.
+
+The available kwargs are:
+
+* `tags`: Specify a list of tags.
+* `component_name`: Specify the component name.
+* `operation_id_base`: Specify the resource-name part of operation IDs.
+
+You pass the kwargs when declaring the `AutoSchema` instance on your view:
+
+```
+class PetDetailView(generics.RetrieveUpdateDestroyAPIView):
+ schema = AutoSchema(
+ tags=['Pets'],
+ component_name='Pet',
+ operation_id_base='Pet',
+ )
+ ...
+```
+
+Assuming a `Pet` model and `PetSerializer` serializer, the kwargs in this
+example are probably not needed. Often, though, you'll need to pass the kwargs
+if you have multiple view targeting the same model, or have multiple views with
+identically named serializers.
+
+If your views have related customizations that are needed frequently, you can
+create a base `AutoSchema` subclass for your project that takes additional
+`__init__()` kwargs to save subclassing `AutoSchema` for each view.
+
+[cite]: https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api
+[openapi]: https://github.com/OAI/OpenAPI-Specification
+[openapi-specification-extensions]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#specification-extensions
+[openapi-operation]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
+[openapi-tags]: https://swagger.io/specification/#tagObject
+[openapi-operationid]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#fixed-fields-17
+[openapi-components]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
+[openapi-reference]: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
+[openapi-generator]: https://github.com/OpenAPITools/openapi-generator
+[swagger-codegen]: https://github.com/swagger-api/swagger-codegen
+[info-object]: https://swagger.io/specification/#infoObject
+[drf-spectacular]: https://drf-spectacular.readthedocs.io/en/latest/readme.html
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
new file mode 100644
index 000000000..0f355c76d
--- /dev/null
+++ b/docs/api-guide/serializers.md
@@ -0,0 +1,1216 @@
+---
+source:
+ - serializers.py
+---
+
+# Serializers
+
+> Expanding the usefulness of the serializers is something that we would
+like to address. However, it's not a trivial problem, and it
+will take some serious design work.
+>
+> — Russell Keith-Magee, [Django users group][cite]
+
+Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into `JSON`, `XML` or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
+
+The serializers in REST framework work very similarly to Django's `Form` and `ModelForm` classes. We provide a `Serializer` class which gives you a powerful, generic way to control the output of your responses, as well as a `ModelSerializer` class which provides a useful shortcut for creating serializers that deal with model instances and querysets.
+
+## Declaring Serializers
+
+Let's start by creating a simple object we can use for example purposes:
+
+ from datetime import datetime
+
+ class Comment:
+ def __init__(self, email, content, created=None):
+ self.email = email
+ self.content = content
+ self.created = created or datetime.now()
+
+ comment = Comment(email='leila@example.com', content='foo bar')
+
+We'll declare a serializer that we can use to serialize and deserialize data that corresponds to `Comment` objects.
+
+Declaring a serializer looks very similar to declaring a form:
+
+ from rest_framework import serializers
+
+ class CommentSerializer(serializers.Serializer):
+ email = serializers.EmailField()
+ content = serializers.CharField(max_length=200)
+ created = serializers.DateTimeField()
+
+## Serializing objects
+
+We can now use `CommentSerializer` to serialize a comment, or list of comments. Again, using the `Serializer` class looks a lot like using a `Form` class.
+
+ serializer = CommentSerializer(comment)
+ serializer.data
+ # {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
+
+At this point we've translated the model instance into Python native datatypes. To finalise the serialization process we render the data into `json`.
+
+ from rest_framework.renderers import JSONRenderer
+
+ json = JSONRenderer().render(serializer.data)
+ json
+ # b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
+
+## Deserializing objects
+
+Deserialization is similar. First we parse a stream into Python native datatypes...
+
+ import io
+ from rest_framework.parsers import JSONParser
+
+ stream = io.BytesIO(json)
+ data = JSONParser().parse(stream)
+
+...then we restore those native datatypes into a dictionary of validated data.
+
+ serializer = CommentSerializer(data=data)
+ serializer.is_valid()
+ # True
+ serializer.validated_data
+ # {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}
+
+## Saving instances
+
+If we want to be able to return complete object instances based on the validated data we need to implement one or both of the `.create()` and `.update()` methods. For example:
+
+ class CommentSerializer(serializers.Serializer):
+ email = serializers.EmailField()
+ content = serializers.CharField(max_length=200)
+ created = serializers.DateTimeField()
+
+ def create(self, validated_data):
+ return Comment(**validated_data)
+
+ def update(self, instance, validated_data):
+ instance.email = validated_data.get('email', instance.email)
+ instance.content = validated_data.get('content', instance.content)
+ instance.created = validated_data.get('created', instance.created)
+ return instance
+
+If your object instances correspond to Django models you'll also want to ensure that these methods save the object to the database. For example, if `Comment` was a Django model, the methods might look like this:
+
+ def create(self, validated_data):
+ return Comment.objects.create(**validated_data)
+
+ def update(self, instance, validated_data):
+ instance.email = validated_data.get('email', instance.email)
+ instance.content = validated_data.get('content', instance.content)
+ instance.created = validated_data.get('created', instance.created)
+ instance.save()
+ return instance
+
+Now when deserializing data, we can call `.save()` to return an object instance, based on the validated data.
+
+ comment = serializer.save()
+
+Calling `.save()` will either create a new instance, or update an existing instance, depending on if an existing instance was passed when instantiating the serializer class:
+
+ # .save() will create a new instance.
+ serializer = CommentSerializer(data=data)
+
+ # .save() will update the existing `comment` instance.
+ serializer = CommentSerializer(comment, data=data)
+
+Both the `.create()` and `.update()` methods are optional. You can implement either none, one, or both of them, depending on the use-case for your serializer class.
+
+#### Passing additional attributes to `.save()`
+
+Sometimes you'll want your view code to be able to inject additional data at the point of saving the instance. This additional data might include information like the current user, the current time, or anything else that is not part of the request data.
+
+You can do so by including additional keyword arguments when calling `.save()`. For example:
+
+ serializer.save(owner=request.user)
+
+Any additional keyword arguments will be included in the `validated_data` argument when `.create()` or `.update()` are called.
+
+#### Overriding `.save()` directly.
+
+In some cases the `.create()` and `.update()` method names may not be meaningful. For example, in a contact form we may not be creating new instances, but instead sending an email or other message.
+
+In these cases you might instead choose to override `.save()` directly, as being more readable and meaningful.
+
+For example:
+
+ class ContactForm(serializers.Serializer):
+ email = serializers.EmailField()
+ message = serializers.CharField()
+
+ def save(self):
+ email = self.validated_data['email']
+ message = self.validated_data['message']
+ send_email(from=email, message=message)
+
+Note that in the case above we're now having to access the serializer `.validated_data` property directly.
+
+## Validation
+
+When deserializing data, you always need to call `is_valid()` before attempting to access the validated data, or save an object instance. If any validation errors occur, the `.errors` property will contain a dictionary representing the resulting error messages. For example:
+
+ serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
+ serializer.is_valid()
+ # False
+ serializer.errors
+ # {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
+
+Each key in the dictionary will be the field name, and the values will be lists of strings of any error messages corresponding to that field. The `non_field_errors` key may also be present, and will list any general validation errors. The name of the `non_field_errors` key may be customized using the `NON_FIELD_ERRORS_KEY` REST framework setting.
+
+When deserializing a list of items, errors will be returned as a list of dictionaries representing each of the deserialized items.
+
+#### Raising an exception on invalid data
+
+The `.is_valid()` method takes an optional `raise_exception` flag that will cause it to raise a `serializers.ValidationError` exception if there are validation errors.
+
+These exceptions are automatically dealt with by the default exception handler that REST framework provides, and will return `HTTP 400 Bad Request` responses by default.
+
+ # Return a 400 response if the data was invalid.
+ serializer.is_valid(raise_exception=True)
+
+#### Field-level validation
+
+You can specify custom field-level validation by adding `.validate_` methods to your `Serializer` subclass. These are similar to the `.clean_` methods on Django forms.
+
+These methods take a single argument, which is the field value that requires validation.
+
+Your `validate_` methods should return the validated value or raise a `serializers.ValidationError`. For example:
+
+ from rest_framework import serializers
+
+ class BlogPostSerializer(serializers.Serializer):
+ title = serializers.CharField(max_length=100)
+ content = serializers.CharField()
+
+ def validate_title(self, value):
+ """
+ Check that the blog post is about Django.
+ """
+ if 'django' not in value.lower():
+ raise serializers.ValidationError("Blog post is not about Django")
+ return value
+
+---
+
+**Note:** If your `` is declared on your serializer with the parameter `required=False` then this validation step will not take place if the field is not included.
+
+---
+
+#### Object-level validation
+
+To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is a dictionary of field values. It should raise a `serializers.ValidationError` if necessary, or just return the validated values. For example:
+
+ from rest_framework import serializers
+
+ class EventSerializer(serializers.Serializer):
+ description = serializers.CharField(max_length=100)
+ start = serializers.DateTimeField()
+ finish = serializers.DateTimeField()
+
+ def validate(self, data):
+ """
+ Check that start is before finish.
+ """
+ if data['start'] > data['finish']:
+ raise serializers.ValidationError("finish must occur after start")
+ return data
+
+#### Validators
+
+Individual fields on a serializer can include validators, by declaring them on the field instance, for example:
+
+ def multiple_of_ten(value):
+ if value % 10 != 0:
+ raise serializers.ValidationError('Not a multiple of ten')
+
+ class GameRecord(serializers.Serializer):
+ score = serializers.IntegerField(validators=[multiple_of_ten])
+ ...
+
+Serializer classes can also include reusable validators that are applied to the complete set of field data. These validators are included by declaring them on an inner `Meta` class, like so:
+
+ class EventSerializer(serializers.Serializer):
+ name = serializers.CharField()
+ room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
+ date = serializers.DateField()
+
+ class Meta:
+ # Each room only has one event per day.
+ validators = [
+ UniqueTogetherValidator(
+ queryset=Event.objects.all(),
+ fields=['room_number', 'date']
+ )
+ ]
+
+For more information see the [validators documentation](validators.md).
+
+## Accessing the initial data and instance
+
+When passing an initial object or queryset to a serializer instance, the object will be made available as `.instance`. If no initial object is passed then the `.instance` attribute will be `None`.
+
+When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the `data` keyword argument is not passed then the `.initial_data` attribute will not exist.
+
+## Partial updates
+
+By default, serializers must be passed values for all required fields or they will raise validation errors. You can use the `partial` argument in order to allow partial updates.
+
+ # Update `comment` with partial data
+ serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
+
+## Dealing with nested objects
+
+The previous examples are fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, where some of the attributes of an object might not be simple datatypes such as strings, dates or integers.
+
+The `Serializer` class is itself a type of `Field`, and can be used to represent relationships where one object type is nested inside another.
+
+ class UserSerializer(serializers.Serializer):
+ email = serializers.EmailField()
+ username = serializers.CharField(max_length=100)
+
+ class CommentSerializer(serializers.Serializer):
+ user = UserSerializer()
+ content = serializers.CharField(max_length=200)
+ created = serializers.DateTimeField()
+
+If a nested representation may optionally accept the `None` value you should pass the `required=False` flag to the nested serializer.
+
+ class CommentSerializer(serializers.Serializer):
+ user = UserSerializer(required=False) # May be an anonymous user.
+ content = serializers.CharField(max_length=200)
+ created = serializers.DateTimeField()
+
+Similarly if a nested representation should be a list of items, you should pass the `many=True` flag to the nested serializer.
+
+ class CommentSerializer(serializers.Serializer):
+ user = UserSerializer(required=False)
+ edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
+ content = serializers.CharField(max_length=200)
+ created = serializers.DateTimeField()
+
+## Writable nested representations
+
+When dealing with nested representations that support deserializing the data, any errors with nested objects will be nested under the field name of the nested object.
+
+ serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
+ serializer.is_valid()
+ # False
+ serializer.errors
+ # {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
+
+Similarly, the `.validated_data` property will include nested data structures.
+
+#### Writing `.create()` methods for nested representations
+
+If you're supporting writable nested representations you'll need to write `.create()` or `.update()` methods that handle saving multiple objects.
+
+The following example demonstrates how you might handle creating a user with a nested profile object.
+
+ class UserSerializer(serializers.ModelSerializer):
+ profile = ProfileSerializer()
+
+ class Meta:
+ model = User
+ fields = ['username', 'email', 'profile']
+
+ def create(self, validated_data):
+ profile_data = validated_data.pop('profile')
+ user = User.objects.create(**validated_data)
+ Profile.objects.create(user=user, **profile_data)
+ return user
+
+#### Writing `.update()` methods for nested representations
+
+For updates you'll want to think carefully about how to handle updates to relationships. For example if the data for the relationship is `None`, or not provided, which of the following should occur?
+
+* Set the relationship to `NULL` in the database.
+* Delete the associated instance.
+* Ignore the data and leave the instance as it is.
+* Raise a validation error.
+
+Here's an example for an `.update()` method on our previous `UserSerializer` class.
+
+ def update(self, instance, validated_data):
+ profile_data = validated_data.pop('profile')
+ # Unless the application properly enforces that this field is
+ # always set, the following could raise a `DoesNotExist`, which
+ # would need to be handled.
+ profile = instance.profile
+
+ instance.username = validated_data.get('username', instance.username)
+ instance.email = validated_data.get('email', instance.email)
+ instance.save()
+
+ profile.is_premium_member = profile_data.get(
+ 'is_premium_member',
+ profile.is_premium_member
+ )
+ profile.has_support_contract = profile_data.get(
+ 'has_support_contract',
+ profile.has_support_contract
+ )
+ profile.save()
+
+ return instance
+
+Because the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
+
+There are however, third-party packages available such as [DRF Writable Nested][thirdparty-writable-nested] that support automatic writable nested representations.
+
+#### Handling saving related instances in model manager classes
+
+An alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances.
+
+For example, suppose we wanted to ensure that `User` instances and `Profile` instances are always created together as a pair. We might write a custom manager class that looks something like this:
+
+ class UserManager(models.Manager):
+ ...
+
+ def create(self, username, email, is_premium_member=False, has_support_contract=False):
+ user = User(username=username, email=email)
+ user.save()
+ profile = Profile(
+ user=user,
+ is_premium_member=is_premium_member,
+ has_support_contract=has_support_contract
+ )
+ profile.save()
+ return user
+
+This manager class now more nicely encapsulates that user instances and profile instances are always created at the same time. Our `.create()` method on the serializer class can now be re-written to use the new manager method.
+
+ def create(self, validated_data):
+ return User.objects.create(
+ username=validated_data['username'],
+ email=validated_data['email'],
+ is_premium_member=validated_data['profile']['is_premium_member'],
+ has_support_contract=validated_data['profile']['has_support_contract']
+ )
+
+For more details on this approach see the Django documentation on [model managers][model-managers], and [this blogpost on using model and manager classes][encapsulation-blogpost].
+
+## Dealing with multiple objects
+
+The `Serializer` class can also handle serializing or deserializing lists of objects.
+
+#### Serializing multiple objects
+
+To serialize a queryset or list of objects instead of a single object instance, you should pass the `many=True` flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
+
+ queryset = Book.objects.all()
+ serializer = BookSerializer(queryset, many=True)
+ serializer.data
+ # [
+ # {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
+ # {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
+ # {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
+ # ]
+
+#### Deserializing multiple objects
+
+The default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the [ListSerializer](#listserializer) documentation below.
+
+## Including extra context
+
+There are some cases where you need to provide extra context to the serializer in addition to the object being serialized. One common case is if you're using a serializer that includes hyperlinked relations, which requires the serializer to have access to the current request so that it can properly generate fully qualified URLs.
+
+You can provide arbitrary additional context by passing a `context` argument when instantiating the serializer. For example:
+
+ serializer = AccountSerializer(account, context={'request': request})
+ serializer.data
+ # {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
+
+The context dictionary can be used within any serializer field logic, such as a custom `.to_representation()` method, by accessing the `self.context` attribute.
+
+---
+
+# ModelSerializer
+
+Often you'll want serializer classes that map closely to Django model definitions.
+
+The `ModelSerializer` class provides a shortcut that lets you automatically create a `Serializer` class with fields that correspond to the Model fields.
+
+**The `ModelSerializer` class is the same as a regular `Serializer` class, except that**:
+
+* It will automatically generate a set of fields for you, based on the model.
+* It will automatically generate validators for the serializer, such as unique_together validators.
+* It includes simple default implementations of `.create()` and `.update()`.
+
+Declaring a `ModelSerializer` looks like this:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['id', 'account_name', 'users', 'created']
+
+By default, all the model fields on the class will be mapped to a corresponding serializer fields.
+
+Any relationships such as foreign keys on the model will be mapped to `PrimaryKeyRelatedField`. Reverse relationships are not included by default unless explicitly included as specified in the [serializer relations][relations] documentation.
+
+#### Inspecting a `ModelSerializer`
+
+Serializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with `ModelSerializers` where you want to determine what set of fields and validators are being automatically created for you.
+
+To do so, open the Django shell, using `python manage.py shell`, then import the serializer class, instantiate it, and print the object representation…
+
+ >>> from myapp.serializers import AccountSerializer
+ >>> serializer = AccountSerializer()
+ >>> print(repr(serializer))
+ AccountSerializer():
+ id = IntegerField(label='ID', read_only=True)
+ name = CharField(allow_blank=True, max_length=100, required=False)
+ owner = PrimaryKeyRelatedField(queryset=User.objects.all())
+
+## Specifying which fields to include
+
+If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`. It is strongly recommended that you explicitly set all fields that should be serialized using the `fields` attribute. This will make it less likely to result in unintentionally exposing data when your models change.
+
+For example:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['id', 'account_name', 'users', 'created']
+
+You can also set the `fields` attribute to the special value `'__all__'` to indicate that all fields in the model should be used.
+
+For example:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ fields = '__all__'
+
+You can set the `exclude` attribute to a list of fields to be excluded from the serializer.
+
+For example:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ exclude = ['users']
+
+In the example above, if the `Account` model had 3 fields `account_name`, `users`, and `created`, this will result in the fields `account_name` and `created` to be serialized.
+
+The names in the `fields` and `exclude` attributes will normally map to model fields on the model class.
+
+Alternatively names in the `fields` options can map to properties or methods which take no arguments that exist on the model class.
+
+Since version 3.3.0, it is **mandatory** to provide one of the attributes `fields` or `exclude`.
+
+## Specifying nested serialization
+
+The default `ModelSerializer` uses primary keys for relationships, but you can also easily generate nested representations using the `depth` option:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['id', 'account_name', 'users', 'created']
+ depth = 1
+
+The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
+
+If you want to customize the way the serialization is done you'll need to define the field yourself.
+
+## Specifying fields explicitly
+
+You can add extra fields to a `ModelSerializer` or override the default fields by declaring fields on the class, just as you would for a `Serializer` class.
+
+ class AccountSerializer(serializers.ModelSerializer):
+ url = serializers.CharField(source='get_absolute_url', read_only=True)
+ groups = serializers.PrimaryKeyRelatedField(many=True)
+
+ class Meta:
+ model = Account
+ fields = ['url', 'groups']
+
+Extra fields can correspond to any property or callable on the model.
+
+## Specifying read only fields
+
+You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the shortcut Meta option, `read_only_fields`.
+
+This option should be a list or tuple of field names, and is declared as follows:
+
+ class AccountSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['id', 'account_name', 'users', 'created']
+ read_only_fields = ['account_name']
+
+Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.
+
+---
+
+**Note**: There is a special-case where a read-only field is part of a `unique_together` constraint at the model level. In this case the field is required by the serializer class in order to validate the constraint, but should also not be editable by the user.
+
+The right way to deal with this is to specify the field explicitly on the serializer, providing both the `read_only=True` and `default=…` keyword arguments.
+
+One example of this is a read-only relation to the currently authenticated `User` which is `unique_together` with another identifier. In this case you would declare the user field like so:
+
+ user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
+
+Please review the [Validators Documentation](/api-guide/validators/) for details on the [UniqueTogetherValidator](/api-guide/validators/#uniquetogethervalidator) and [CurrentUserDefault](/api-guide/validators/#currentuserdefault) classes.
+
+---
+
+
+## Additional keyword arguments
+
+There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. As in the case of `read_only_fields`, this means you do not need to explicitly declare the field on the serializer.
+
+This option is a dictionary, mapping field names to a dictionary of keyword arguments. For example:
+
+ class CreateUserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = User
+ fields = ['email', 'username', 'password']
+ extra_kwargs = {'password': {'write_only': True}}
+
+ def create(self, validated_data):
+ user = User(
+ email=validated_data['email'],
+ username=validated_data['username']
+ )
+ user.set_password(validated_data['password'])
+ user.save()
+ return user
+
+Please keep in mind that, if the field has already been explicitly declared on the serializer class, then the `extra_kwargs` option will be ignored.
+
+## Relational fields
+
+When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for `ModelSerializer` is to use the primary keys of the related instances.
+
+Alternative representations include serializing using hyperlinks, serializing complete nested representations, or serializing with a custom representation.
+
+For full details see the [serializer relations][relations] documentation.
+
+## Customizing field mappings
+
+The ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.
+
+Normally if a `ModelSerializer` does not generate the fields you need by default then you should either add them to the class explicitly, or simply use a regular `Serializer` class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.
+
+### `serializer_field_mapping`
+
+A mapping of Django model fields to REST framework serializer fields. You can override this mapping to alter the default serializer fields that should be used for each model field.
+
+### `serializer_related_field`
+
+This property should be the serializer field class, that is used for relational fields by default.
+
+For `ModelSerializer` this defaults to `serializers.PrimaryKeyRelatedField`.
+
+For `HyperlinkedModelSerializer` this defaults to `serializers.HyperlinkedRelatedField`.
+
+### `serializer_url_field`
+
+The serializer field class that should be used for any `url` field on the serializer.
+
+Defaults to `serializers.HyperlinkedIdentityField`
+
+### `serializer_choice_field`
+
+The serializer field class that should be used for any choice fields on the serializer.
+
+Defaults to `serializers.ChoiceField`
+
+### The field_class and field_kwargs API
+
+The following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of `(field_class, field_kwargs)`.
+
+### `build_standard_field(self, field_name, model_field)`
+
+Called to generate a serializer field that maps to a standard model field.
+
+The default implementation returns a serializer class based on the `serializer_field_mapping` attribute.
+
+### `build_relational_field(self, field_name, relation_info)`
+
+Called to generate a serializer field that maps to a relational model field.
+
+The default implementation returns a serializer class based on the `serializer_related_field` attribute.
+
+The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
+
+### `build_nested_field(self, field_name, relation_info, nested_depth)`
+
+Called to generate a serializer field that maps to a relational model field, when the `depth` option has been set.
+
+The default implementation dynamically creates a nested serializer class based on either `ModelSerializer` or `HyperlinkedModelSerializer`.
+
+The `nested_depth` will be the value of the `depth` option, minus one.
+
+The `relation_info` argument is a named tuple, that contains `model_field`, `related_model`, `to_many` and `has_through_model` properties.
+
+### `build_property_field(self, field_name, model_class)`
+
+Called to generate a serializer field that maps to a property or zero-argument method on the model class.
+
+The default implementation returns a `ReadOnlyField` class.
+
+### `build_url_field(self, field_name, model_class)`
+
+Called to generate a serializer field for the serializer's own `url` field. The default implementation returns a `HyperlinkedIdentityField` class.
+
+### `build_unknown_field(self, field_name, model_class)`
+
+Called when the field name did not map to any model field or model property.
+The default implementation raises an error, although subclasses may customize this behavior.
+
+---
+
+# HyperlinkedModelSerializer
+
+The `HyperlinkedModelSerializer` class is similar to the `ModelSerializer` class except that it uses hyperlinks to represent relationships, rather than primary keys.
+
+By default the serializer will include a `url` field instead of a primary key field.
+
+The url field will be represented using a `HyperlinkedIdentityField` serializer field, and any relationships on the model will be represented using a `HyperlinkedRelatedField` serializer field.
+
+You can explicitly include the primary key by adding it to the `fields` option, for example:
+
+ class AccountSerializer(serializers.HyperlinkedModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['url', 'id', 'account_name', 'users', 'created']
+
+## Absolute and relative URLs
+
+When instantiating a `HyperlinkedModelSerializer` you must include the current
+`request` in the serializer context, for example:
+
+ serializer = AccountSerializer(queryset, context={'request': request})
+
+Doing so will ensure that the hyperlinks can include an appropriate hostname,
+so that the resulting representation uses fully qualified URLs, such as:
+
+ http://api.example.com/accounts/1/
+
+Rather than relative URLs, such as:
+
+ /accounts/1/
+
+If you *do* want to use relative URLs, you should explicitly pass `{'request': None}`
+in the serializer context.
+
+## How hyperlinked views are determined
+
+There needs to be a way of determining which views should be used for hyperlinking to model instances.
+
+By default hyperlinks are expected to correspond to a view name that matches the style `'{model_name}-detail'`, and looks up the instance by a `pk` keyword argument.
+
+You can override a URL field view name and lookup field by using either, or both of, the `view_name` and `lookup_field` options in the `extra_kwargs` setting, like so:
+
+ class AccountSerializer(serializers.HyperlinkedModelSerializer):
+ class Meta:
+ model = Account
+ fields = ['account_url', 'account_name', 'users', 'created']
+ extra_kwargs = {
+ 'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
+ 'users': {'lookup_field': 'username'}
+ }
+
+Alternatively you can set the fields on the serializer explicitly. For example:
+
+ class AccountSerializer(serializers.HyperlinkedModelSerializer):
+ url = serializers.HyperlinkedIdentityField(
+ view_name='accounts',
+ lookup_field='slug'
+ )
+ users = serializers.HyperlinkedRelatedField(
+ view_name='user-detail',
+ lookup_field='username',
+ many=True,
+ read_only=True
+ )
+
+ class Meta:
+ model = Account
+ fields = ['url', 'account_name', 'users', 'created']
+
+---
+
+**Tip**: Properly matching together hyperlinked representations and your URL conf can sometimes be a bit fiddly. Printing the `repr` of a `HyperlinkedModelSerializer` instance is a particularly useful way to inspect exactly which view names and lookup fields the relationships are expected to map too.
+
+---
+
+## Changing the URL field name
+
+The name of the URL field defaults to 'url'. You can override this globally, by using the `URL_FIELD_NAME` setting.
+
+---
+
+# ListSerializer
+
+The `ListSerializer` class provides the behavior for serializing and validating multiple objects at once. You won't *typically* need to use `ListSerializer` directly, but should instead simply pass `many=True` when instantiating a serializer.
+
+When a serializer is instantiated and `many=True` is passed, a `ListSerializer` instance will be created. The serializer class then becomes a child of the parent `ListSerializer`
+
+The following argument can also be passed to a `ListSerializer` field or a serializer that is passed `many=True`:
+
+### `allow_empty`
+
+This is `True` by default, but can be set to `False` if you want to disallow empty lists as valid input.
+
+### `max_length`
+
+This is `None` by default, but can be set to a positive integer if you want to validate that the list contains no more than this number of elements.
+
+### `min_length`
+
+This is `None` by default, but can be set to a positive integer if you want to validate that the list contains no fewer than this number of elements.
+
+### Customizing `ListSerializer` behavior
+
+There *are* a few use cases when you might want to customize the `ListSerializer` behavior. For example:
+
+* You want to provide particular validation of the lists, such as checking that one element does not conflict with another element in a list.
+* You want to customize the create or update behavior of multiple objects.
+
+For these cases you can modify the class that is used when `many=True` is passed, by using the `list_serializer_class` option on the serializer `Meta` class.
+
+For example:
+
+ class CustomListSerializer(serializers.ListSerializer):
+ ...
+
+ class CustomSerializer(serializers.Serializer):
+ ...
+ class Meta:
+ list_serializer_class = CustomListSerializer
+
+#### Customizing multiple create
+
+The default implementation for multiple object creation is to simply call `.create()` for each item in the list. If you want to customize this behavior, you'll need to customize the `.create()` method on `ListSerializer` class that is used when `many=True` is passed.
+
+For example:
+
+ class BookListSerializer(serializers.ListSerializer):
+ def create(self, validated_data):
+ books = [Book(**item) for item in validated_data]
+ return Book.objects.bulk_create(books)
+
+ class BookSerializer(serializers.Serializer):
+ ...
+ class Meta:
+ list_serializer_class = BookListSerializer
+
+#### Customizing multiple update
+
+By default the `ListSerializer` class does not support multiple updates. This is because the behavior that should be expected for insertions and deletions is ambiguous.
+
+To support multiple updates you'll need to do so explicitly. When writing your multiple update code make sure to keep the following in mind:
+
+* How do you determine which instance should be updated for each item in the list of data?
+* How should insertions be handled? Are they invalid, or do they create new objects?
+* How should removals be handled? Do they imply object deletion, or removing a relationship? Should they be silently ignored, or are they invalid?
+* How should ordering be handled? Does changing the position of two items imply any state change or is it ignored?
+
+You will need to add an explicit `id` field to the instance serializer. The default implicitly-generated `id` field is marked as `read_only`. This causes it to be removed on updates. Once you declare it explicitly, it will be available in the list serializer's `update` method.
+
+Here's an example of how you might choose to implement multiple updates:
+
+ class BookListSerializer(serializers.ListSerializer):
+ def update(self, instance, validated_data):
+ # Maps for id->instance and id->data item.
+ book_mapping = {book.id: book for book in instance}
+ data_mapping = {item['id']: item for item in validated_data}
+
+ # Perform creations and updates.
+ ret = []
+ for book_id, data in data_mapping.items():
+ book = book_mapping.get(book_id, None)
+ if book is None:
+ ret.append(self.child.create(data))
+ else:
+ ret.append(self.child.update(book, data))
+
+ # Perform deletions.
+ for book_id, book in book_mapping.items():
+ if book_id not in data_mapping:
+ book.delete()
+
+ return ret
+
+ class BookSerializer(serializers.Serializer):
+ # We need to identify elements in the list using their primary key,
+ # so use a writable field here, rather than the default which would be read-only.
+ id = serializers.IntegerField()
+ ...
+
+ class Meta:
+ list_serializer_class = BookListSerializer
+
+It is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the `allow_add_remove` behavior that was present in REST framework 2.
+
+#### Customizing ListSerializer initialization
+
+When a serializer with `many=True` is instantiated, we need to determine which arguments and keyword arguments should be passed to the `.__init__()` method for both the child `Serializer` class, and for the parent `ListSerializer` class.
+
+The default implementation is to pass all arguments to both classes, except for `validators`, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.
+
+Occasionally you might need to explicitly specify how the child and parent classes should be instantiated when `many=True` is passed. You can do so by using the `many_init` class method.
+
+ @classmethod
+ def many_init(cls, *args, **kwargs):
+ # Instantiate the child serializer.
+ kwargs['child'] = cls()
+ # Instantiate the parent list serializer.
+ return CustomListSerializer(*args, **kwargs)
+
+---
+
+# BaseSerializer
+
+`BaseSerializer` class that can be used to easily support alternative serialization and deserialization styles.
+
+This class implements the same basic API as the `Serializer` class:
+
+* `.data` - Returns the outgoing primitive representation.
+* `.is_valid()` - Deserializes and validates incoming data.
+* `.validated_data` - Returns the validated incoming data.
+* `.errors` - Returns any errors during validation.
+* `.save()` - Persists the validated data into an object instance.
+
+There are four methods that can be overridden, depending on what functionality you want the serializer class to support:
+
+* `.to_representation()` - Override this to support serialization, for read operations.
+* `.to_internal_value()` - Override this to support deserialization, for write operations.
+* `.create()` and `.update()` - Override either or both of these to support saving instances.
+
+Because this class provides the same interface as the `Serializer` class, you can use it with the existing generic class-based views exactly as you would for a regular `Serializer` or `ModelSerializer`.
+
+The only difference you'll notice when doing so is the `BaseSerializer` classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input.
+
+#### Read-only `BaseSerializer` classes
+
+To implement a read-only serializer using the `BaseSerializer` class, we just need to override the `.to_representation()` method. Let's take a look at an example using a simple Django model:
+
+ class HighScore(models.Model):
+ created = models.DateTimeField(auto_now_add=True)
+ player_name = models.CharField(max_length=10)
+ score = models.IntegerField()
+
+It's simple to create a read-only serializer for converting `HighScore` instances into primitive data types.
+
+ class HighScoreSerializer(serializers.BaseSerializer):
+ def to_representation(self, instance):
+ return {
+ 'score': instance.score,
+ 'player_name': instance.player_name
+ }
+
+We can now use this class to serialize single `HighScore` instances:
+
+ @api_view(['GET'])
+ def high_score(request, pk):
+ instance = HighScore.objects.get(pk=pk)
+ serializer = HighScoreSerializer(instance)
+ return Response(serializer.data)
+
+Or use it to serialize multiple instances:
+
+ @api_view(['GET'])
+ def all_high_scores(request):
+ queryset = HighScore.objects.order_by('-score')
+ serializer = HighScoreSerializer(queryset, many=True)
+ return Response(serializer.data)
+
+#### Read-write `BaseSerializer` classes
+
+To create a read-write serializer we first need to implement a `.to_internal_value()` method. This method returns the validated values that will be used to construct the object instance, and may raise a `serializers.ValidationError` if the supplied data is in an incorrect format.
+
+Once you've implemented `.to_internal_value()`, the basic validation API will be available on the serializer, and you will be able to use `.is_valid()`, `.validated_data` and `.errors`.
+
+If you want to also support `.save()` you'll need to also implement either or both of the `.create()` and `.update()` methods.
+
+Here's a complete example of our previous `HighScoreSerializer`, that's been updated to support both read and write operations.
+
+ class HighScoreSerializer(serializers.BaseSerializer):
+ def to_internal_value(self, data):
+ score = data.get('score')
+ player_name = data.get('player_name')
+
+ # Perform the data validation.
+ if not score:
+ raise serializers.ValidationError({
+ 'score': 'This field is required.'
+ })
+ if not player_name:
+ raise serializers.ValidationError({
+ 'player_name': 'This field is required.'
+ })
+ if len(player_name) > 10:
+ raise serializers.ValidationError({
+ 'player_name': 'May not be more than 10 characters.'
+ })
+
+ # Return the validated values. This will be available as
+ # the `.validated_data` property.
+ return {
+ 'score': int(score),
+ 'player_name': player_name
+ }
+
+ def to_representation(self, instance):
+ return {
+ 'score': instance.score,
+ 'player_name': instance.player_name
+ }
+
+ def create(self, validated_data):
+ return HighScore.objects.create(**validated_data)
+
+#### Creating new base classes
+
+The `BaseSerializer` class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends.
+
+The following class is an example of a generic serializer that can handle coercing arbitrary complex objects into primitive representations.
+
+ class ObjectSerializer(serializers.BaseSerializer):
+ """
+ A read-only serializer that coerces arbitrary complex objects
+ into primitive representations.
+ """
+ def to_representation(self, instance):
+ output = {}
+ for attribute_name in dir(instance):
+ attribute = getattr(instance, attribute_name)
+ if attribute_name.startswith('_'):
+ # Ignore private attributes.
+ pass
+ elif hasattr(attribute, '__call__'):
+ # Ignore methods and other callables.
+ pass
+ elif isinstance(attribute, (str, int, bool, float, type(None))):
+ # Primitive types can be passed through unmodified.
+ output[attribute_name] = attribute
+ elif isinstance(attribute, list):
+ # Recursively deal with items in lists.
+ output[attribute_name] = [
+ self.to_representation(item) for item in attribute
+ ]
+ elif isinstance(attribute, dict):
+ # Recursively deal with items in dictionaries.
+ output[attribute_name] = {
+ str(key): self.to_representation(value)
+ for key, value in attribute.items()
+ }
+ else:
+ # Force anything else to its string representation.
+ output[attribute_name] = str(attribute)
+ return output
+
+---
+
+# Advanced serializer usage
+
+## Overriding serialization and deserialization behavior
+
+If you need to alter the serialization or deserialization behavior of a serializer class, you can do so by overriding the `.to_representation()` or `.to_internal_value()` methods.
+
+Some reasons this might be useful include...
+
+* Adding new behavior for new serializer base classes.
+* Modifying the behavior slightly for an existing class.
+* Improving serialization performance for a frequently accessed API endpoint that returns lots of data.
+
+The signatures for these methods are as follows:
+
+#### `to_representation(self, instance)`
+
+Takes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API.
+
+May be overridden in order to modify the representation style. For example:
+
+ def to_representation(self, instance):
+ """Convert `username` to lowercase."""
+ ret = super().to_representation(instance)
+ ret['username'] = ret['username'].lower()
+ return ret
+
+#### ``to_internal_value(self, data)``
+
+Takes the unvalidated incoming data as input and should return the validated data that will be made available as `serializer.validated_data`. The return value will also be passed to the `.create()` or `.update()` methods if `.save()` is called on the serializer class.
+
+If any of the validation fails, then the method should raise a `serializers.ValidationError(errors)`. The `errors` argument should be a dictionary mapping field names (or `settings.NON_FIELD_ERRORS_KEY`) to a list of error messages. If you don't need to alter deserialization behavior and instead want to provide object-level validation, it's recommended that you instead override the [`.validate()`](#object-level-validation) method.
+
+The `data` argument passed to this method will normally be the value of `request.data`, so the datatype it provides will depend on the parser classes you have configured for your API.
+
+## Serializer Inheritance
+
+Similar to Django forms, you can extend and reuse serializers through inheritance. This allows you to declare a common set of fields or methods on a parent class that can then be used in a number of serializers. For example,
+
+ class MyBaseSerializer(Serializer):
+ my_field = serializers.CharField()
+
+ def validate_my_field(self, value):
+ ...
+
+ class MySerializer(MyBaseSerializer):
+ ...
+
+Like Django's `Model` and `ModelForm` classes, the inner `Meta` class on serializers does not implicitly inherit from it's parents' inner `Meta` classes. If you want the `Meta` class to inherit from a parent class you must do so explicitly. For example:
+
+ class AccountSerializer(MyBaseSerializer):
+ class Meta(MyBaseSerializer.Meta):
+ model = Account
+
+Typically we would recommend *not* using inheritance on inner Meta classes, but instead declaring all options explicitly.
+
+Additionally, the following caveats apply to serializer inheritance:
+
+* Normal Python name resolution rules apply. If you have multiple base classes that declare a `Meta` inner class, only the first one will be used. This means the child’s `Meta`, if it exists, otherwise the `Meta` of the first parent, etc.
+* It’s possible to declaratively remove a `Field` inherited from a parent class by setting the name to be `None` on the subclass.
+
+ class MyBaseSerializer(ModelSerializer):
+ my_field = serializers.CharField()
+
+ class MySerializer(MyBaseSerializer):
+ my_field = None
+
+ However, you can only use this technique to opt out from a field defined declaratively by a parent class; it won’t prevent the `ModelSerializer` from generating a default field. To opt-out from default fields, see [Specifying which fields to include](#specifying-which-fields-to-include).
+
+## Dynamically modifying fields
+
+Once a serializer has been initialized, the dictionary of fields that are set on the serializer may be accessed using the `.fields` attribute. Accessing and modifying this attribute allows you to dynamically modify the serializer.
+
+Modifying the `fields` argument directly allows you to do interesting things such as changing the arguments on serializer fields at runtime, rather than at the point of declaring the serializer.
+
+### Example
+
+For example, if you wanted to be able to set which fields should be used by a serializer at the point of initializing it, you could create a serializer class like so:
+
+ class DynamicFieldsModelSerializer(serializers.ModelSerializer):
+ """
+ A ModelSerializer that takes an additional `fields` argument that
+ controls which fields should be displayed.
+ """
+
+ def __init__(self, *args, **kwargs):
+ # Don't pass the 'fields' arg up to the superclass
+ fields = kwargs.pop('fields', None)
+
+ # Instantiate the superclass normally
+ super().__init__(*args, **kwargs)
+
+ if fields is not None:
+ # Drop any fields that are not specified in the `fields` argument.
+ allowed = set(fields)
+ existing = set(self.fields)
+ for field_name in existing - allowed:
+ self.fields.pop(field_name)
+
+This would then allow you to do the following:
+
+ >>> class UserSerializer(DynamicFieldsModelSerializer):
+ >>> class Meta:
+ >>> model = User
+ >>> fields = ['id', 'username', 'email']
+ >>>
+ >>> print(UserSerializer(user))
+ {'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
+ >>>
+ >>> print(UserSerializer(user, fields=('id', 'email')))
+ {'id': 2, 'email': 'jon@example.com'}
+
+## Customizing the default fields
+
+REST framework 2 provided an API to allow developers to override how a `ModelSerializer` class would automatically generate the default set of fields.
+
+This API included the `.get_field()`, `.get_pk_field()` and other methods.
+
+Because the serializers have been fundamentally redesigned with 3.0 this API no longer exists. You can still modify the fields that get created but you'll need to refer to the source code, and be aware that if the changes you make are against private bits of API then they may be subject to change.
+
+---
+
+# Third party packages
+
+The following third party packages are also available.
+
+## Django REST marshmallow
+
+The [django-rest-marshmallow][django-rest-marshmallow] package provides an alternative implementation for serializers, using the python [marshmallow][marshmallow] library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases.
+
+## Serpy
+
+The [serpy][serpy] package is an alternative implementation for serializers that is built for speed. [Serpy][serpy] serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed.
+
+## MongoengineModelSerializer
+
+The [django-rest-framework-mongoengine][mongoengine] package provides a `MongoEngineModelSerializer` serializer class that supports using MongoDB as the storage layer for Django REST framework.
+
+## GeoFeatureModelSerializer
+
+The [django-rest-framework-gis][django-rest-framework-gis] package provides a `GeoFeatureModelSerializer` serializer class that supports GeoJSON both for read and write operations.
+
+## HStoreSerializer
+
+The [django-rest-framework-hstore][django-rest-framework-hstore] package provides an `HStoreSerializer` to support [django-hstore][django-hstore] `DictionaryField` model field and its `schema-mode` feature.
+
+## Dynamic REST
+
+The [dynamic-rest][dynamic-rest] package extends the ModelSerializer and ModelViewSet interfaces, adding API query parameters for filtering, sorting, and including / excluding all fields and relationships defined by your serializers.
+
+## Dynamic Fields Mixin
+
+The [drf-dynamic-fields][drf-dynamic-fields] package provides a mixin to dynamically limit the fields per serializer to a subset specified by an URL parameter.
+
+## DRF FlexFields
+
+The [drf-flex-fields][drf-flex-fields] package extends the ModelSerializer and ModelViewSet to provide commonly used functionality for dynamically setting fields and expanding primitive fields to nested models, both from URL parameters and your serializer class definitions.
+
+## Serializer Extensions
+
+The [django-rest-framework-serializer-extensions][drf-serializer-extensions]
+package provides a collection of tools to DRY up your serializers, by allowing
+fields to be defined on a per-view/request basis. Fields can be whitelisted,
+blacklisted and child serializers can be optionally expanded.
+
+## HTML JSON Forms
+
+The [html-json-forms][html-json-forms] package provides an algorithm and serializer for processing `
+
+Django REST framework is a powerful and flexible toolkit for building Web APIs.
+
+Some reasons you might want to use REST framework:
+
+* The [Web browsable API][sandbox] is a huge usability win for your developers.
+* [Authentication policies][authentication] including packages for [OAuth1a][oauth1-section] and [OAuth2][oauth2-section].
+* [Serialization][serializers] that supports both [ORM][modelserializer-section] and [non-ORM][serializer-section] data sources.
+* Customizable all the way down - just use [regular function-based views][functionview-section] if you don't need the [more][generic-views] [powerful][viewsets] [features][routers].
+* Extensive documentation, and [great community support][group].
+* Used and trusted by internationally recognised companies including [Mozilla][mozilla], [Red Hat][redhat], [Heroku][heroku], and [Eventbrite][eventbrite].
+
+---
+
+## Funding
+
+REST framework is a *collaboratively funded project*. If you use
+REST framework commercially we strongly encourage you to invest in its
+continued development by **[signing up for a paid plan][funding]**.
+
+*Every single sign-up helps us make REST framework long-term financially sustainable.*
+
+
+ {% endblock %}
+
+You can also customize the style by adding the `bootstrap_theme` or `style` block similar to `api.html`.
+
+### Advanced Customization
+
+#### Context
+
+The context that's available to the template:
+
+* `allowed_methods` : A list of methods allowed by the resource
+* `api_settings` : The API settings
+* `available_formats` : A list of formats allowed by the resource
+* `breadcrumblist` : The list of links following the chain of nested resources
+* `content` : The content of the API response
+* `description` : The description of the resource, generated from its docstring
+* `name` : The name of the resource
+* `post_form` : A form instance for use by the POST form (if allowed)
+* `put_form` : A form instance for use by the PUT form (if allowed)
+* `display_edit_forms` : A boolean indicating whether or not POST, PUT and PATCH forms will be displayed
+* `request` : The request object
+* `response` : The response object
+* `version` : The version of Django REST Framework
+* `view` : The view handling the request
+* `FORMAT_PARAM` : The view can accept a format override
+* `METHOD_PARAM` : The view can accept a method override
+
+You can override the `BrowsableAPIRenderer.get_context()` method to customise the context that gets passed to the template.
+
+#### Not using base.html
+
+For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you.
+
+#### Handling `ChoiceField` with large numbers of items.
+
+When a relationship or `ChoiceField` has too many items, rendering the widget containing all the options can become very slow, and cause the browsable API rendering to perform poorly.
+
+The simplest option in this case is to replace the select input with a standard text input. For example:
+
+ author = serializers.HyperlinkedRelatedField(
+ queryset=User.objects.all(),
+ style={'base_template': 'input.html'}
+ )
+
+#### Autocomplete
+
+An alternative, but more complex option would be to replace the input with an autocomplete widget, that only loads and renders a subset of the available options as needed. If you need to do this you'll need to do some work to build a custom autocomplete HTML template yourself.
+
+There are [a variety of packages for autocomplete widgets][autocomplete-packages], such as [django-autocomplete-light][django-autocomplete-light], that you may want to refer to. Note that you will not be able to simply include these components as standard widgets, but will need to write the HTML template explicitly. This is because REST framework 3.0 no longer supports the `widget` keyword argument since it now uses templated HTML generation.
+
+---
+
+[cite]: https://en.wikiquote.org/wiki/Alfred_North_Whitehead
+[drfreverse]: ../api-guide/reverse.md
+[ffjsonview]: https://addons.mozilla.org/en-US/firefox/addon/jsonview/
+[chromejsonview]: https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc
+[bootstrap]: https://getbootstrap.com/
+[cerulean]: ../img/cerulean.png
+[slate]: ../img/slate.png
+[bswatch]: https://bootswatch.com/
+[bcomponents]: https://getbootstrap.com/2.3.2/components.html
+[bcomponentsnav]: https://getbootstrap.com/2.3.2/components.html#navbar
+[autocomplete-packages]: https://www.djangopackages.com/grids/g/auto-complete/
+[django-autocomplete-light]: https://github.com/yourlabs/django-autocomplete-light
diff --git a/docs/topics/browser-enhancements.md b/docs/topics/browser-enhancements.md
new file mode 100644
index 000000000..67c1c1898
--- /dev/null
+++ b/docs/topics/browser-enhancements.md
@@ -0,0 +1,89 @@
+# Browser enhancements
+
+> "There are two noncontroversial uses for overloaded POST. The first is to *simulate* HTTP's uniform interface for clients like web browsers that don't support PUT or DELETE"
+>
+> — [RESTful Web Services][cite], Leonard Richardson & Sam Ruby.
+
+In order to allow the browsable API to function, there are a couple of browser enhancements that REST framework needs to provide.
+
+As of version 3.3.0 onwards these are enabled with javascript, using the [ajax-form][ajax-form] library.
+
+## Browser based PUT, DELETE, etc...
+
+The [AJAX form library][ajax-form] supports browser-based `PUT`, `DELETE` and other methods on HTML forms.
+
+After including the library, use the `data-method` attribute on the form, like so:
+
+
+
+ ...
+
+
+Note that prior to 3.3.0, this support was server-side rather than javascript based. The method overloading style (as used in [Ruby on Rails][rails]) is no longer supported due to subtle issues that it introduces in request parsing.
+
+## Browser based submission of non-form content
+
+Browser-based submission of content types such as JSON are supported by the [AJAX form library][ajax-form], using form fields with `data-override='content-type'` and `data-override='content'` attributes.
+
+For example:
+
+
+
+
+
+
+
+Note that prior to 3.3.0, this support was server-side rather than javascript based.
+
+## URL based format suffixes
+
+REST framework can take `?format=json` style URL parameters, which can be a
+useful shortcut for determining which content type should be returned from
+the view.
+
+This behavior is controlled using the `URL_FORMAT_OVERRIDE` setting.
+
+## HTTP header based method overriding
+
+Prior to version 3.3.0 the semi extension header `X-HTTP-Method-Override` was supported for overriding the request method. This behavior is no longer in core, but can be adding if needed using middleware.
+
+For example:
+
+ METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE'
+
+ class MethodOverrideMiddleware:
+
+ def __init__(self, get_response):
+ self.get_response = get_response
+
+ def __call__(self, request):
+ if request.method == 'POST' and METHOD_OVERRIDE_HEADER in request.META:
+ request.method = request.META[METHOD_OVERRIDE_HEADER]
+ return self.get_response(request)
+
+## URL based accept headers
+
+Until version 3.3.0 REST framework included built-in support for `?accept=application/json` style URL parameters, which would allow the `Accept` header to be overridden.
+
+Since the introduction of the content negotiation API this behavior is no longer included in core, but may be added using a custom content negotiation class, if needed.
+
+For example:
+
+ class AcceptQueryParamOverride()
+ def get_accept_list(self, request):
+ header = request.META.get('HTTP_ACCEPT', '*/*')
+ header = request.query_params.get('_accept', header)
+ return [token.strip() for token in header.split(',')]
+
+## Doesn't HTML5 support PUT and DELETE forms?
+
+Nope. It was at one point intended to support `PUT` and `DELETE` forms, but
+was later [dropped from the spec][html5]. There remains
+[ongoing discussion][put_delete] about adding support for `PUT` and `DELETE`,
+as well as how to support content types other than form-encoded data.
+
+[cite]: https://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260
+[ajax-form]: https://github.com/tomchristie/ajax-form
+[rails]: https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work
+[html5]: https://www.w3.org/TR/html5-diff/#changes-2010-06-24
+[put_delete]: http://amundsen.com/examples/put-delete-forms/
diff --git a/docs/topics/documenting-your-api.md b/docs/topics/documenting-your-api.md
new file mode 100644
index 000000000..b85d6310a
--- /dev/null
+++ b/docs/topics/documenting-your-api.md
@@ -0,0 +1,235 @@
+# Documenting your API
+
+> A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state.
+>
+> — Roy Fielding, [REST APIs must be hypertext driven][cite]
+
+REST framework provides a range of different choices for documenting your API. The following
+is a non-exhaustive list of the most popular ones.
+
+## Third party packages for OpenAPI support
+
+### drf-spectacular
+
+[drf-spectacular][drf-spectacular] is an [OpenAPI 3][open-api] schema generation library with explicit
+focus on extensibility, customizability and client generation. It is the recommended way for
+generating and presenting OpenAPI schemas.
+
+The library aims to extract as much schema information as possible, while providing decorators and extensions for easy
+customization. There is explicit support for [swagger-codegen][swagger], [SwaggerUI][swagger-ui] and [Redoc][redoc],
+i18n, versioning, authentication, polymorphism (dynamic requests and responses), query/path/header parameters,
+documentation and more. Several popular plugins for DRF are supported out-of-the-box as well.
+
+### drf-yasg
+
+[drf-yasg][drf-yasg] is a [Swagger / OpenAPI 2][swagger] generation tool implemented without using the schema generation provided
+by Django Rest Framework.
+
+It aims to implement as much of the [OpenAPI 2][open-api] specification as possible - nested schemas, named models,
+response bodies, enum/pattern/min/max validators, form parameters, etc. - and to generate documents usable with code
+generation tools like `swagger-codegen`.
+
+This also translates into a very useful interactive documentation viewer in the form of `swagger-ui`:
+
+![Screenshot - drf-yasg][image-drf-yasg]
+
+---
+
+## Built-in OpenAPI schema generation (deprecated)
+
+**Deprecation notice: REST framework's built-in support for generating OpenAPI schemas is
+deprecated in favor of 3rd party packages that can provide this functionality instead.
+As replacement, we recommend using the [drf-spectacular](#drf-spectacular) package.**
+
+There are a number of packages available that allow you to generate HTML
+documentation pages from OpenAPI schemas.
+
+Two popular options are [Swagger UI][swagger-ui] and [ReDoc][redoc].
+
+Both require little more than the location of your static schema file or
+dynamic `SchemaView` endpoint.
+
+### A minimal example with Swagger UI
+
+Assuming you've followed the example from the schemas documentation for routing
+a dynamic `SchemaView`, a minimal Django template for using Swagger UI might be
+this:
+
+```html
+
+
+
+ Swagger
+
+
+
+
+
+
+
+
+
+
+```
+
+Save this in your templates folder as `swagger-ui.html`. Then route a
+`TemplateView` in your project's URL conf:
+
+```python
+from django.views.generic import TemplateView
+
+urlpatterns = [
+ # ...
+ # Route TemplateView to serve Swagger UI template.
+ # * Provide `extra_context` with view name of `SchemaView`.
+ path('swagger-ui/', TemplateView.as_view(
+ template_name='swagger-ui.html',
+ extra_context={'schema_url':'openapi-schema'}
+ ), name='swagger-ui'),
+]
+```
+
+See the [Swagger UI documentation][swagger-ui] for advanced usage.
+
+### A minimal example with ReDoc.
+
+Assuming you've followed the example from the schemas documentation for routing
+a dynamic `SchemaView`, a minimal Django template for using ReDoc might be
+this:
+
+```html
+
+
+
+ ReDoc
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Save this in your templates folder as `redoc.html`. Then route a `TemplateView`
+in your project's URL conf:
+
+```python
+from django.views.generic import TemplateView
+
+urlpatterns = [
+ # ...
+ # Route TemplateView to serve the ReDoc template.
+ # * Provide `extra_context` with view name of `SchemaView`.
+ path('redoc/', TemplateView.as_view(
+ template_name='redoc.html',
+ extra_context={'schema_url':'openapi-schema'}
+ ), name='redoc'),
+]
+```
+
+See the [ReDoc documentation][redoc] for advanced usage.
+
+
+## Self describing APIs
+
+The browsable API that REST framework provides makes it possible for your API to be entirely self describing. The documentation for each API endpoint can be provided simply by visiting the URL in your browser.
+
+![Screenshot - Self describing API][image-self-describing-api]
+
+---
+
+#### Setting the title
+
+The title that is used in the browsable API is generated from the view class name or function name. Any trailing `View` or `ViewSet` suffix is stripped, and the string is whitespace separated on uppercase/lowercase boundaries or underscores.
+
+For example, the view `UserListView`, will be named `User List` when presented in the browsable API.
+
+When working with viewsets, an appropriate suffix is appended to each generated view. For example, the view set `UserViewSet` will generate views named `User List` and `User Instance`.
+
+#### Setting the description
+
+The description in the browsable API is generated from the docstring of the view or viewset.
+
+If the python `Markdown` library is installed, then [markdown syntax][markdown] may be used in the docstring, and will be converted to HTML in the browsable API. For example:
+
+ class AccountListView(views.APIView):
+ """
+ Returns a list of all **active** accounts in the system.
+
+ For more details on how accounts are activated please [see here][ref].
+
+ [ref]: http://example.com/activating-accounts
+ """
+
+Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples].
+
+#### The `OPTIONS` method
+
+REST framework APIs also support programmatically accessible descriptions, using the `OPTIONS` HTTP method. A view will respond to an `OPTIONS` request with metadata including the name, description, and the various media types it accepts and responds with.
+
+When using the generic views, any `OPTIONS` requests will additionally respond with metadata regarding any `POST` or `PUT` actions available, describing which fields are on the serializer.
+
+You can modify the response behavior to `OPTIONS` requests by overriding the `options` view method and/or by providing a custom Metadata class. For example:
+
+ def options(self, request, *args, **kwargs):
+ """
+ Don't include the view description in OPTIONS responses.
+ """
+ meta = self.metadata_class()
+ data = meta.determine_metadata(request, self)
+ data.pop('description')
+ return Response(data=data, status=status.HTTP_200_OK)
+
+See [the Metadata docs][metadata-docs] for more details.
+
+---
+
+## The hypermedia approach
+
+To be fully RESTful an API should present its available actions as hypermedia controls in the responses that it sends.
+
+In this approach, rather than documenting the available API endpoints up front, the description instead concentrates on the *media types* that are used. The available actions that may be taken on any given URL are not strictly fixed, but are instead made available by the presence of link and form controls in the returned document.
+
+To implement a hypermedia API you'll need to decide on an appropriate media type for the API, and implement a custom renderer and parser for that media type. The [REST, Hypermedia & HATEOAS][hypermedia-docs] section of the documentation includes pointers to background reading, as well as links to various hypermedia formats.
+
+[cite]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
+
+[hypermedia-docs]: rest-hypermedia-hateoas.md
+[metadata-docs]: ../api-guide/metadata.md
+[schemas-examples]: ../api-guide/schemas.md#examples
+
+[image-drf-yasg]: ../img/drf-yasg.png
+[image-self-describing-api]: ../img/self-describing.png
+
+[drf-yasg]: https://github.com/axnsan12/drf-yasg/
+[drf-spectacular]: https://github.com/tfranzel/drf-spectacular/
+[markdown]: https://daringfireball.net/projects/markdown/syntax
+[open-api]: https://openapis.org/
+[redoc]: https://github.com/Rebilly/ReDoc
+[swagger]: https://swagger.io/
+[swagger-ui]: https://swagger.io/tools/swagger-ui/
diff --git a/docs/topics/html-and-forms.md b/docs/topics/html-and-forms.md
new file mode 100644
index 000000000..c7e51c152
--- /dev/null
+++ b/docs/topics/html-and-forms.md
@@ -0,0 +1,220 @@
+# HTML & Forms
+
+REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can be used as HTML forms and rendered in templates.
+
+## Rendering HTML
+
+In order to return HTML responses you'll need to use either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`.
+
+The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response.
+
+The `StaticHTMLRender` class expects the response to contain a string of the pre-rendered HTML content.
+
+Because static HTML pages typically have different behavior from API responses you'll probably need to write any HTML views explicitly, rather than relying on the built-in generic views.
+
+Here's an example of a view that returns a list of "Profile" instances, rendered in an HTML template:
+
+**views.py**:
+
+ from my_project.example.models import Profile
+ from rest_framework.renderers import TemplateHTMLRenderer
+ from rest_framework.response import Response
+ from rest_framework.views import APIView
+
+
+ class ProfileList(APIView):
+ renderer_classes = [TemplateHTMLRenderer]
+ template_name = 'profile_list.html'
+
+ def get(self, request):
+ queryset = Profile.objects.all()
+ return Response({'profiles': queryset})
+
+**profile_list.html**:
+
+
+
Profiles
+
+ {% for profile in profiles %}
+
{{ profile.name }}
+ {% endfor %}
+
+
+
+## Rendering Forms
+
+Serializers may be rendered as forms by using the `render_form` template tag, and including the serializer instance as context to the template.
+
+The following view demonstrates an example of using a serializer in a template for viewing and updating a model instance:
+
+**views.py**:
+
+ from django.shortcuts import get_object_or_404
+ from my_project.example.models import Profile
+ from rest_framework.renderers import TemplateHTMLRenderer
+ from rest_framework.views import APIView
+
+
+ class ProfileDetail(APIView):
+ renderer_classes = [TemplateHTMLRenderer]
+ template_name = 'profile_detail.html'
+
+ def get(self, request, pk):
+ profile = get_object_or_404(Profile, pk=pk)
+ serializer = ProfileSerializer(profile)
+ return Response({'serializer': serializer, 'profile': profile})
+
+ def post(self, request, pk):
+ profile = get_object_or_404(Profile, pk=pk)
+ serializer = ProfileSerializer(profile, data=request.data)
+ if not serializer.is_valid():
+ return Response({'serializer': serializer, 'profile': profile})
+ serializer.save()
+ return redirect('profile-list')
+
+**profile_detail.html**:
+
+ {% load rest_framework %}
+
+
+
+
+
+
+
+### Using template packs
+
+The `render_form` tag takes an optional `template_pack` argument, that specifies which template directory should be used for rendering the form and form fields.
+
+REST framework includes three built-in template packs, all based on Bootstrap 3. The built-in styles are `horizontal`, `vertical`, and `inline`. The default style is `horizontal`. To use any of these template packs you'll want to also include the Bootstrap 3 CSS.
+
+The following HTML will link to a CDN hosted version of the Bootstrap 3 CSS:
+
+
+ …
+
+
+
+Third party packages may include alternate template packs, by bundling a template directory containing the necessary form and field templates.
+
+Let's take a look at how to render each of the three available template packs. For these examples we'll use a single serializer class to present a "Login" form.
+
+ class LoginSerializer(serializers.Serializer):
+ email = serializers.EmailField(
+ max_length=100,
+ style={'placeholder': 'Email', 'autofocus': True}
+ )
+ password = serializers.CharField(
+ max_length=100,
+ style={'input_type': 'password', 'placeholder': 'Password'}
+ )
+ remember_me = serializers.BooleanField()
+
+---
+
+#### `rest_framework/vertical`
+
+Presents form labels above their corresponding control inputs, using the standard Bootstrap layout.
+
+*This is the default template pack.*
+
+ {% load rest_framework %}
+
+ ...
+
+
+
+
+
+---
+
+#### `rest_framework/horizontal`
+
+Presents labels and controls alongside each other, using a 2/10 column split.
+
+*This is the form style used in the browsable API and admin renderers.*
+
+ {% load rest_framework %}
+
+ ...
+
+
+
+
+
+## Field styles
+
+Serializer fields can have their rendering style customized by using the `style` keyword argument. This argument is a dictionary of options that control the template and layout used.
+
+The most common way to customize the field style is to use the `base_template` style keyword argument to select which template in the template pack should be use.
+
+For example, to render a `CharField` as an HTML textarea rather than the default HTML input, you would use something like this:
+
+ details = serializers.CharField(
+ max_length=1000,
+ style={'base_template': 'textarea.html'}
+ )
+
+If you instead want a field to be rendered using a custom template that is *not part of an included template pack*, you can instead use the `template` style option, to fully specify a template name:
+
+ details = serializers.CharField(
+ max_length=1000,
+ style={'template': 'my-field-templates/custom-input.html'}
+ )
+
+Field templates can also use additional style properties, depending on their type. For example, the `textarea.html` template also accepts a `rows` property that can be used to affect the sizing of the control.
+
+ details = serializers.CharField(
+ max_length=1000,
+ style={'base_template': 'textarea.html', 'rows': 10}
+ )
+
+The complete list of `base_template` options and their associated style options is listed below.
+
+base_template | Valid field types | Additional style options
+-----------------------|-------------------------------------------------------------|-----------------------------------------------
+input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label, autofocus
+textarea.html | `CharField` | rows, placeholder, hide_label
+select.html | `ChoiceField` or relational field types | hide_label
+radio.html | `ChoiceField` or relational field types | inline, hide_label
+select_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | hide_label
+checkbox_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | inline, hide_label
+checkbox.html | `BooleanField` | hide_label
+fieldset.html | Nested serializer | hide_label
+list_fieldset.html | `ListField` or nested serializer with `many=True` | hide_label
diff --git a/docs/topics/internationalization.md b/docs/topics/internationalization.md
new file mode 100644
index 000000000..267ccdb37
--- /dev/null
+++ b/docs/topics/internationalization.md
@@ -0,0 +1,112 @@
+# Internationalization
+
+> Supporting internationalization is not optional. It must be a core feature.
+>
+> — [Jannis Leidel, speaking at Django Under the Hood, 2015][cite].
+
+REST framework ships with translatable error messages. You can make these appear in your language enabling [Django's standard translation mechanisms][django-translation].
+
+Doing so will allow you to:
+
+* Select a language other than English as the default, using the standard `LANGUAGE_CODE` Django setting.
+* Allow clients to choose a language themselves, using the `LocaleMiddleware` included with Django. A typical usage for API clients would be to include an `Accept-Language` request header.
+
+## Enabling internationalized APIs
+
+You can change the default language by using the standard Django `LANGUAGE_CODE` setting:
+
+ LANGUAGE_CODE = "es-es"
+
+You can turn on per-request language requests by adding `LocalMiddleware` to your `MIDDLEWARE` setting:
+
+ MIDDLEWARE = [
+ ...
+ 'django.middleware.locale.LocaleMiddleware'
+ ]
+
+When per-request internationalization is enabled, client requests will respect the `Accept-Language` header where possible. For example, let's make a request for an unsupported media type:
+
+**Request**
+
+ GET /api/users HTTP/1.1
+ Accept: application/xml
+ Accept-Language: es-es
+ Host: example.org
+
+**Response**
+
+ HTTP/1.0 406 NOT ACCEPTABLE
+
+ {"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."}
+
+REST framework includes these built-in translations both for standard exception cases, and for serializer validation errors.
+
+Note that the translations only apply to the error strings themselves. The format of error messages, and the keys of field names will remain the same. An example `400 Bad Request` response body might look like this:
+
+ {"detail": {"username": ["Esse campo deve ser único."]}}
+
+If you want to use different string for parts of the response such as `detail` and `non_field_errors` then you can modify this behavior by using a [custom exception handler][custom-exception-handler].
+
+#### Specifying the set of supported languages.
+
+By default all available languages will be supported.
+
+If you only wish to support a subset of the available languages, use Django's standard `LANGUAGES` setting:
+
+ LANGUAGES = [
+ ('de', _('German')),
+ ('en', _('English')),
+ ]
+
+## Adding new translations
+
+REST framework translations are managed online using [Transifex][transifex-project]. You can use the Transifex service to add new translation languages. The maintenance team will then ensure that these translation strings are included in the REST framework package.
+
+Sometimes you may need to add translation strings to your project locally. You may need to do this if:
+
+* You want to use REST Framework in a language which has not been translated yet on Transifex.
+* Your project includes custom error messages, which are not part of REST framework's default translation strings.
+
+#### Translating a new language locally
+
+This guide assumes you are already familiar with how to translate a Django app. If you're not, start by reading [Django's translation docs][django-translation].
+
+If you're translating a new language you'll need to translate the existing REST framework error messages:
+
+1. Make a new folder where you want to store the internationalization resources. Add this path to your [`LOCALE_PATHS`][django-locale-paths] setting.
+
+2. Now create a subfolder for the language you want to translate. The folder should be named using [locale name][django-locale-name] notation. For example: `de`, `pt_BR`, `es_AR`.
+
+3. Now copy the [base translations file][django-po-source] from the REST framework source code into your translations folder.
+
+4. Edit the `django.po` file you've just copied, translating all the error messages.
+
+5. Run `manage.py compilemessages -l pt_BR` to make the translations
+available for Django to use. You should see a message like `processing file django.po in <...>/locale/pt_BR/LC_MESSAGES`.
+
+6. Restart your development server to see the changes take effect.
+
+If you're only translating custom error messages that exist inside your project codebase you don't need to copy the REST framework source `django.po` file into a `LOCALE_PATHS` folder, and can instead simply run Django's standard `makemessages` process.
+
+## How the language is determined
+
+If you want to allow per-request language preferences you'll need to include `django.middleware.locale.LocaleMiddleware` in your `MIDDLEWARE` setting.
+
+You can find more information on how the language preference is determined in the [Django documentation][django-language-preference]. For reference, the method is:
+
+1. First, it looks for the language prefix in the requested URL.
+2. Failing that, it looks for the `LANGUAGE_SESSION_KEY` key in the current user’s session.
+3. Failing that, it looks for a cookie.
+4. Failing that, it looks at the `Accept-Language` HTTP header.
+5. Failing that, it uses the global `LANGUAGE_CODE` setting.
+
+For API clients the most appropriate of these will typically be to use the `Accept-Language` header; Sessions and cookies will not be available unless using session authentication, and generally better practice to prefer an `Accept-Language` header for API clients rather than using language URL prefixes.
+
+[cite]: https://youtu.be/Wa0VfS2q94Y
+[django-translation]: https://docs.djangoproject.com/en/stable/topics/i18n/translation
+[custom-exception-handler]: ../api-guide/exceptions.md#custom-exception-handling
+[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
+[django-po-source]: https://raw.githubusercontent.com/encode/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po
+[django-language-preference]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#how-django-discovers-language-preference
+[django-locale-paths]: https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-LOCALE_PATHS
+[django-locale-name]: https://docs.djangoproject.com/en/stable/topics/i18n/#term-locale-name
diff --git a/docs/topics/rest-hypermedia-hateoas.md b/docs/topics/rest-hypermedia-hateoas.md
new file mode 100644
index 000000000..3498bddd1
--- /dev/null
+++ b/docs/topics/rest-hypermedia-hateoas.md
@@ -0,0 +1,54 @@
+# REST, Hypermedia & HATEOAS
+
+> You keep using that word "REST". I do not think it means what you think it means.
+>
+> — Mike Amundsen, [REST fest 2012 keynote][cite].
+
+First off, the disclaimer. The name "Django REST framework" was decided back in early 2011 and was chosen simply to ensure the project would be easily found by developers. Throughout the documentation we try to use the more simple and technically correct terminology of "Web APIs".
+
+If you are serious about designing a Hypermedia API, you should look to resources outside of this documentation to help inform your design choices.
+
+The following fall into the "required reading" category.
+
+* Roy Fielding's dissertation - [Architectural Styles and
+the Design of Network-based Software Architectures][dissertation].
+* Roy Fielding's "[REST APIs must be hypertext-driven][hypertext-driven]" blog post.
+* Leonard Richardson & Mike Amundsen's [RESTful Web APIs][restful-web-apis].
+* Mike Amundsen's [Building Hypermedia APIs with HTML5 and Node][building-hypermedia-apis].
+* Steve Klabnik's [Designing Hypermedia APIs][designing-hypermedia-apis].
+* The [Richardson Maturity Model][maturitymodel].
+
+For a more thorough background, check out Klabnik's [Hypermedia API reading list][readinglist].
+
+## Building Hypermedia APIs with REST framework
+
+REST framework is an agnostic Web API toolkit. It does help guide you towards building well-connected APIs, and makes it easy to design appropriate media types, but it does not strictly enforce any particular design style.
+
+## What REST framework provides.
+
+It is self evident that REST framework makes it possible to build Hypermedia APIs. The browsable API that it offers is built on HTML - the hypermedia language of the web.
+
+REST framework also includes [serialization] and [parser]/[renderer] components that make it easy to build appropriate media types, [hyperlinked relations][fields] for building well-connected systems, and great support for [content negotiation][conneg].
+
+## What REST framework doesn't provide.
+
+What REST framework doesn't do is give you machine readable hypermedia formats such as [HAL][hal], [Collection+JSON][collection], [JSON API][json-api] or HTML [microformats] by default, or the ability to auto-magically create fully HATEOAS style APIs that include hypermedia-based form descriptions and semantically labelled hyperlinks. Doing so would involve making opinionated choices about API design that should really remain outside of the framework's scope.
+
+[cite]: https://vimeo.com/channels/restfest/49503453
+[dissertation]: https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
+[hypertext-driven]: https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
+[restful-web-apis]: http://restfulwebapis.org/
+[building-hypermedia-apis]: https://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578
+[designing-hypermedia-apis]: http://designinghypermediaapis.com/
+[readinglist]: http://blog.steveklabnik.com/posts/2012-02-27-hypermedia-api-reading-list
+[maturitymodel]: https://martinfowler.com/articles/richardsonMaturityModel.html
+
+[hal]: http://stateless.co/hal_specification.html
+[collection]: http://www.amundsen.com/media-types/collection/
+[json-api]: http://jsonapi.org/
+[microformats]: http://microformats.org/wiki/Main_Page
+[serialization]: ../api-guide/serializers.md
+[parser]: ../api-guide/parsers.md
+[renderer]: ../api-guide/renderers.md
+[fields]: ../api-guide/fields.md
+[conneg]: ../api-guide/content-negotiation.md
diff --git a/docs/topics/writable-nested-serializers.md b/docs/topics/writable-nested-serializers.md
new file mode 100644
index 000000000..3bac84ffa
--- /dev/null
+++ b/docs/topics/writable-nested-serializers.md
@@ -0,0 +1,47 @@
+> To save HTTP requests, it may be convenient to send related documents along with the request.
+>
+> — [JSON API specification for Ember Data][cite].
+
+# Writable nested serializers
+
+Although flat data structures serve to properly delineate between the individual entities in your service, there are cases where it may be more appropriate or convenient to use nested data structures.
+
+Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go. However, there are a few more subtleties to using writable nested serializers, due to the dependencies between the various model instances, and the need to save or delete multiple instances in a single action.
+
+## One-to-many data structures
+
+*Example of a **read-only** nested serializer. Nothing complex to worry about here.*
+
+ class ToDoItemSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ToDoItem
+ fields = ['text', 'is_completed']
+
+ class ToDoListSerializer(serializers.ModelSerializer):
+ items = ToDoItemSerializer(many=True, read_only=True)
+
+ class Meta:
+ model = ToDoList
+ fields = ['title', 'items']
+
+Some example output from our serializer.
+
+ {
+ 'title': 'Leaving party preparations',
+ 'items': [
+ {'text': 'Compile playlist', 'is_completed': True},
+ {'text': 'Send invites', 'is_completed': False},
+ {'text': 'Clean house', 'is_completed': False}
+ ]
+ }
+
+Let's take a look at updating our nested one-to-many data structure.
+
+### Validation errors
+
+### Adding and removing items
+
+### Making PATCH requests
+
+
+[cite]: http://jsonapi.org/format/#url-based-json-api
diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md
new file mode 100644
index 000000000..6db3ea282
--- /dev/null
+++ b/docs/tutorial/1-serialization.md
@@ -0,0 +1,378 @@
+# Tutorial 1: Serialization
+
+## Introduction
+
+This tutorial will cover creating a simple pastebin code highlighting Web API. Along the way it will introduce the various components that make up REST framework, and give you a comprehensive understanding of how everything fits together.
+
+The tutorial is fairly in-depth, so you should probably get a cookie and a cup of your favorite brew before getting started. If you just want a quick overview, you should head over to the [quickstart] documentation instead.
+
+---
+
+**Note**: The code for this tutorial is available in the [encode/rest-framework-tutorial][repo] repository on GitHub. The completed implementation is also online as a sandbox version for testing, [available here][sandbox].
+
+---
+
+## Setting up a new environment
+
+Before we do anything else we'll create a new virtual environment, using [venv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
+
+ python3 -m venv env
+ source env/bin/activate
+
+Now that we're inside a virtual environment, we can install our package requirements.
+
+ pip install django
+ pip install djangorestframework
+ pip install pygments # We'll be using this for the code highlighting
+
+**Note:** To exit the virtual environment at any time, just type `deactivate`. For more information see the [venv documentation][venv].
+
+## Getting started
+
+Okay, we're ready to get coding.
+To get started, let's create a new project to work with.
+
+ cd ~
+ django-admin startproject tutorial
+ cd tutorial
+
+Once that's done we can create an app that we'll use to create a simple Web API.
+
+ python manage.py startapp snippets
+
+We'll need to add our new `snippets` app and the `rest_framework` app to `INSTALLED_APPS`. Let's edit the `tutorial/settings.py` file:
+
+ INSTALLED_APPS = [
+ ...
+ 'rest_framework',
+ 'snippets',
+ ]
+
+Okay, we're ready to roll.
+
+## Creating a model to work with
+
+For the purposes of this tutorial we're going to start by creating a simple `Snippet` model that is used to store code snippets. Go ahead and edit the `snippets/models.py` file. Note: Good programming practices include comments. Although you will find them in our repository version of this tutorial code, we have omitted them here to focus on the code itself.
+
+ from django.db import models
+ from pygments.lexers import get_all_lexers
+ from pygments.styles import get_all_styles
+
+ LEXERS = [item for item in get_all_lexers() if item[1]]
+ LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
+ STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])
+
+
+ class Snippet(models.Model):
+ created = models.DateTimeField(auto_now_add=True)
+ title = models.CharField(max_length=100, blank=True, default='')
+ code = models.TextField()
+ linenos = models.BooleanField(default=False)
+ language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
+ style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
+
+ class Meta:
+ ordering = ['created']
+
+We'll also need to create an initial migration for our snippet model, and sync the database for the first time.
+
+ python manage.py makemigrations snippets
+ python manage.py migrate snippets
+
+## Creating a Serializer class
+
+The first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as `json`. We can do this by declaring serializers that work very similar to Django's forms. Create a file in the `snippets` directory named `serializers.py` and add the following.
+
+ from rest_framework import serializers
+ from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
+
+
+ class SnippetSerializer(serializers.Serializer):
+ id = serializers.IntegerField(read_only=True)
+ title = serializers.CharField(required=False, allow_blank=True, max_length=100)
+ code = serializers.CharField(style={'base_template': 'textarea.html'})
+ linenos = serializers.BooleanField(required=False)
+ language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
+ style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
+
+ def create(self, validated_data):
+ """
+ Create and return a new `Snippet` instance, given the validated data.
+ """
+ return Snippet.objects.create(**validated_data)
+
+ def update(self, instance, validated_data):
+ """
+ Update and return an existing `Snippet` instance, given the validated data.
+ """
+ instance.title = validated_data.get('title', instance.title)
+ instance.code = validated_data.get('code', instance.code)
+ instance.linenos = validated_data.get('linenos', instance.linenos)
+ instance.language = validated_data.get('language', instance.language)
+ instance.style = validated_data.get('style', instance.style)
+ instance.save()
+ return instance
+
+The first part of the serializer class defines the fields that get serialized/deserialized. The `create()` and `update()` methods define how fully fledged instances are created or modified when calling `serializer.save()`
+
+A serializer class is very similar to a Django `Form` class, and includes similar validation flags on the various fields, such as `required`, `max_length` and `default`.
+
+The field flags can also control how the serializer should be displayed in certain circumstances, such as when rendering to HTML. The `{'base_template': 'textarea.html'}` flag above is equivalent to using `widget=widgets.Textarea` on a Django `Form` class. This is particularly useful for controlling how the browsable API should be displayed, as we'll see later in the tutorial.
+
+We can actually also save ourselves some time by using the `ModelSerializer` class, as we'll see later, but for now we'll keep our serializer definition explicit.
+
+## Working with Serializers
+
+Before we go any further we'll familiarize ourselves with using our new Serializer class. Let's drop into the Django shell.
+
+ python manage.py shell
+
+Okay, once we've got a few imports out of the way, let's create a couple of code snippets to work with.
+
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+ from rest_framework.renderers import JSONRenderer
+ from rest_framework.parsers import JSONParser
+
+ snippet = Snippet(code='foo = "bar"\n')
+ snippet.save()
+
+ snippet = Snippet(code='print("hello, world")\n')
+ snippet.save()
+
+We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances.
+
+ serializer = SnippetSerializer(snippet)
+ serializer.data
+ # {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}
+
+At this point we've translated the model instance into Python native datatypes. To finalize the serialization process we render the data into `json`.
+
+ content = JSONRenderer().render(serializer.data)
+ content
+ # b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false, "language": "python", "style": "friendly"}'
+
+Deserialization is similar. First we parse a stream into Python native datatypes...
+
+ import io
+
+ stream = io.BytesIO(content)
+ data = JSONParser().parse(stream)
+
+...then we restore those native datatypes into a fully populated object instance.
+
+ serializer = SnippetSerializer(data=data)
+ serializer.is_valid()
+ # True
+ serializer.validated_data
+ # OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
+ serializer.save()
+ #
+
+Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.
+
+We can also serialize querysets instead of model instances. To do so we simply add a `many=True` flag to the serializer arguments.
+
+ serializer = SnippetSerializer(Snippet.objects.all(), many=True)
+ serializer.data
+ # [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
+
+## Using ModelSerializers
+
+Our `SnippetSerializer` class is replicating a lot of information that's also contained in the `Snippet` model. It would be nice if we could keep our code a bit more concise.
+
+In the same way that Django provides both `Form` classes and `ModelForm` classes, REST framework includes both `Serializer` classes, and `ModelSerializer` classes.
+
+Let's look at refactoring our serializer using the `ModelSerializer` class.
+Open the file `snippets/serializers.py` again, and replace the `SnippetSerializer` class with the following.
+
+ class SnippetSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Snippet
+ fields = ['id', 'title', 'code', 'linenos', 'language', 'style']
+
+One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing its representation. Open the Django shell with `python manage.py shell`, then try the following:
+
+ from snippets.serializers import SnippetSerializer
+ serializer = SnippetSerializer()
+ print(repr(serializer))
+ # SnippetSerializer():
+ # id = IntegerField(label='ID', read_only=True)
+ # title = CharField(allow_blank=True, max_length=100, required=False)
+ # code = CharField(style={'base_template': 'textarea.html'})
+ # linenos = BooleanField(required=False)
+ # language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
+ # style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
+
+It's important to remember that `ModelSerializer` classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes:
+
+* An automatically determined set of fields.
+* Simple default implementations for the `create()` and `update()` methods.
+
+## Writing regular Django views using our Serializer
+
+Let's see how we can write some API views using our new Serializer class.
+For the moment we won't use any of REST framework's other features, we'll just write the views as regular Django views.
+
+Edit the `snippets/views.py` file, and add the following.
+
+ from django.http import HttpResponse, JsonResponse
+ from django.views.decorators.csrf import csrf_exempt
+ from rest_framework.parsers import JSONParser
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+
+The root of our API is going to be a view that supports listing all the existing snippets, or creating a new snippet.
+
+ @csrf_exempt
+ def snippet_list(request):
+ """
+ List all code snippets, or create a new snippet.
+ """
+ if request.method == 'GET':
+ snippets = Snippet.objects.all()
+ serializer = SnippetSerializer(snippets, many=True)
+ return JsonResponse(serializer.data, safe=False)
+
+ elif request.method == 'POST':
+ data = JSONParser().parse(request)
+ serializer = SnippetSerializer(data=data)
+ if serializer.is_valid():
+ serializer.save()
+ return JsonResponse(serializer.data, status=201)
+ return JsonResponse(serializer.errors, status=400)
+
+Note that because we want to be able to POST to this view from clients that won't have a CSRF token we need to mark the view as `csrf_exempt`. This isn't something that you'd normally want to do, and REST framework views actually use more sensible behavior than this, but it'll do for our purposes right now.
+
+We'll also need a view which corresponds to an individual snippet, and can be used to retrieve, update or delete the snippet.
+
+ @csrf_exempt
+ def snippet_detail(request, pk):
+ """
+ Retrieve, update or delete a code snippet.
+ """
+ try:
+ snippet = Snippet.objects.get(pk=pk)
+ except Snippet.DoesNotExist:
+ return HttpResponse(status=404)
+
+ if request.method == 'GET':
+ serializer = SnippetSerializer(snippet)
+ return JsonResponse(serializer.data)
+
+ elif request.method == 'PUT':
+ data = JSONParser().parse(request)
+ serializer = SnippetSerializer(snippet, data=data)
+ if serializer.is_valid():
+ serializer.save()
+ return JsonResponse(serializer.data)
+ return JsonResponse(serializer.errors, status=400)
+
+ elif request.method == 'DELETE':
+ snippet.delete()
+ return HttpResponse(status=204)
+
+Finally we need to wire these views up. Create the `snippets/urls.py` file:
+
+ from django.urls import path
+ from snippets import views
+
+ urlpatterns = [
+ path('snippets/', views.snippet_list),
+ path('snippets//', views.snippet_detail),
+ ]
+
+We also need to wire up the root urlconf, in the `tutorial/urls.py` file, to include our snippet app's URLs.
+
+ from django.urls import path, include
+
+ urlpatterns = [
+ path('', include('snippets.urls')),
+ ]
+
+It's worth noting that there are a couple of edge cases we're not dealing with properly at the moment. If we send malformed `json`, or if a request is made with a method that the view doesn't handle, then we'll end up with a 500 "server error" response. Still, this'll do for now.
+
+## Testing our first attempt at a Web API
+
+Now we can start up a sample server that serves our snippets.
+
+Quit out of the shell...
+
+ quit()
+
+...and start up Django's development server.
+
+ python manage.py runserver
+
+ Validating models...
+
+ 0 errors found
+ Django version 4.0, using settings 'tutorial.settings'
+ Starting Development server at http://127.0.0.1:8000/
+ Quit the server with CONTROL-C.
+
+In another terminal window, we can test the server.
+
+We can test our API using [curl][curl] or [httpie][httpie]. Httpie is a user friendly http client that's written in Python. Let's install that.
+
+You can install httpie using pip:
+
+ pip install httpie
+
+Finally, we can get a list of all of the snippets:
+
+ http http://127.0.0.1:8000/snippets/
+
+ HTTP/1.1 200 OK
+ ...
+ [
+ {
+ "id": 1,
+ "title": "",
+ "code": "foo = \"bar\"\n",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ },
+ {
+ "id": 2,
+ "title": "",
+ "code": "print(\"hello, world\")\n",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+ ]
+
+Or we can get a particular snippet by referencing its id:
+
+ http http://127.0.0.1:8000/snippets/2/
+
+ HTTP/1.1 200 OK
+ ...
+ {
+ "id": 2,
+ "title": "",
+ "code": "print(\"hello, world\")\n",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+
+Similarly, you can have the same json displayed by visiting these URLs in a web browser.
+
+## Where are we now
+
+We're doing okay so far, we've got a serialization API that feels pretty similar to Django's Forms API, and some regular Django views.
+
+Our API views don't do anything particularly special at the moment, beyond serving `json` responses, and there are some error handling edge cases we'd still like to clean up, but it's a functioning Web API.
+
+We'll see how we can start to improve things in [part 2 of the tutorial][tut-2].
+
+[quickstart]: quickstart.md
+[repo]: https://github.com/encode/rest-framework-tutorial
+[sandbox]: https://restframework.herokuapp.com/
+[venv]: https://docs.python.org/3/library/venv.html
+[tut-2]: 2-requests-and-responses.md
+[httpie]: https://github.com/httpie/httpie#installation
+[curl]: https://curl.haxx.se/
diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md
new file mode 100644
index 000000000..47c7facfc
--- /dev/null
+++ b/docs/tutorial/2-requests-and-responses.md
@@ -0,0 +1,207 @@
+# Tutorial 2: Requests and Responses
+
+From this point we're going to really start covering the core of REST framework.
+Let's introduce a couple of essential building blocks.
+
+## Request objects
+
+REST framework introduces a `Request` object that extends the regular `HttpRequest`, and provides more flexible request parsing. The core functionality of the `Request` object is the `request.data` attribute, which is similar to `request.POST`, but more useful for working with Web APIs.
+
+ request.POST # Only handles form data. Only works for 'POST' method.
+ request.data # Handles arbitrary data. Works for 'POST', 'PUT' and 'PATCH' methods.
+
+## Response objects
+
+REST framework also introduces a `Response` object, which is a type of `TemplateResponse` that takes unrendered content and uses content negotiation to determine the correct content type to return to the client.
+
+ return Response(data) # Renders to content type as requested by the client.
+
+## Status codes
+
+Using numeric HTTP status codes in your views doesn't always make for obvious reading, and it's easy to not notice if you get an error code wrong. REST framework provides more explicit identifiers for each status code, such as `HTTP_400_BAD_REQUEST` in the `status` module. It's a good idea to use these throughout rather than using numeric identifiers.
+
+## Wrapping API views
+
+REST framework provides two wrappers you can use to write API views.
+
+1. The `@api_view` decorator for working with function based views.
+2. The `APIView` class for working with class-based views.
+
+These wrappers provide a few bits of functionality such as making sure you receive `Request` instances in your view, and adding context to `Response` objects so that content negotiation can be performed.
+
+The wrappers also provide behavior such as returning `405 Method Not Allowed` responses when appropriate, and handling any `ParseError` exceptions that occur when accessing `request.data` with malformed input.
+
+## Pulling it all together
+
+Okay, let's go ahead and start using these new components to refactor our views slightly.
+
+ from rest_framework import status
+ from rest_framework.decorators import api_view
+ from rest_framework.response import Response
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+
+
+ @api_view(['GET', 'POST'])
+ def snippet_list(request):
+ """
+ List all code snippets, or create a new snippet.
+ """
+ if request.method == 'GET':
+ snippets = Snippet.objects.all()
+ serializer = SnippetSerializer(snippets, many=True)
+ return Response(serializer.data)
+
+ elif request.method == 'POST':
+ serializer = SnippetSerializer(data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+Our instance view is an improvement over the previous example. It's a little more concise, and the code now feels very similar to if we were working with the Forms API. We're also using named status codes, which makes the response meanings more obvious.
+
+Here is the view for an individual snippet, in the `views.py` module.
+
+ @api_view(['GET', 'PUT', 'DELETE'])
+ def snippet_detail(request, pk):
+ """
+ Retrieve, update or delete a code snippet.
+ """
+ try:
+ snippet = Snippet.objects.get(pk=pk)
+ except Snippet.DoesNotExist:
+ return Response(status=status.HTTP_404_NOT_FOUND)
+
+ if request.method == 'GET':
+ serializer = SnippetSerializer(snippet)
+ return Response(serializer.data)
+
+ elif request.method == 'PUT':
+ serializer = SnippetSerializer(snippet, data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ elif request.method == 'DELETE':
+ snippet.delete()
+ return Response(status=status.HTTP_204_NO_CONTENT)
+
+This should all feel very familiar - it is not a lot different from working with regular Django views.
+
+Notice that we're no longer explicitly tying our requests or responses to a given content type. `request.data` can handle incoming `json` requests, but it can also handle other formats. Similarly we're returning response objects with data, but allowing REST framework to render the response into the correct content type for us.
+
+## Adding optional format suffixes to our URLs
+
+To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as [http://example.com/api/items/4.json][json-url].
+
+Start by adding a `format` keyword argument to both of the views, like so.
+
+ def snippet_list(request, format=None):
+
+and
+
+ def snippet_detail(request, pk, format=None):
+
+Now update the `snippets/urls.py` file slightly, to append a set of `format_suffix_patterns` in addition to the existing URLs.
+
+ from django.urls import path
+ from rest_framework.urlpatterns import format_suffix_patterns
+ from snippets import views
+
+ urlpatterns = [
+ path('snippets/', views.snippet_list),
+ path('snippets//', views.snippet_detail),
+ ]
+
+ urlpatterns = format_suffix_patterns(urlpatterns)
+
+We don't necessarily need to add these extra url patterns in, but it gives us a simple, clean way of referring to a specific format.
+
+## How's it looking?
+
+Go ahead and test the API from the command line, as we did in [tutorial part 1][tut-1]. Everything is working pretty similarly, although we've got some nicer error handling if we send invalid requests.
+
+We can get a list of all of the snippets, as before.
+
+ http http://127.0.0.1:8000/snippets/
+
+ HTTP/1.1 200 OK
+ ...
+ [
+ {
+ "id": 1,
+ "title": "",
+ "code": "foo = \"bar\"\n",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ },
+ {
+ "id": 2,
+ "title": "",
+ "code": "print(\"hello, world\")\n",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+ ]
+
+We can control the format of the response that we get back, either by using the `Accept` header:
+
+ http http://127.0.0.1:8000/snippets/ Accept:application/json # Request JSON
+ http http://127.0.0.1:8000/snippets/ Accept:text/html # Request HTML
+
+Or by appending a format suffix:
+
+ http http://127.0.0.1:8000/snippets.json # JSON suffix
+ http http://127.0.0.1:8000/snippets.api # Browsable API suffix
+
+Similarly, we can control the format of the request that we send, using the `Content-Type` header.
+
+ # POST using form data
+ http --form POST http://127.0.0.1:8000/snippets/ code="print(123)"
+
+ {
+ "id": 3,
+ "title": "",
+ "code": "print(123)",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+
+ # POST using JSON
+ http --json POST http://127.0.0.1:8000/snippets/ code="print(456)"
+
+ {
+ "id": 4,
+ "title": "",
+ "code": "print(456)",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+
+If you add a `--debug` switch to the `http` requests above, you will be able to see the request type in request headers.
+
+Now go and open the API in a web browser, by visiting [http://127.0.0.1:8000/snippets/][devserver].
+
+### Browsability
+
+Because the API chooses the content type of the response based on the client request, it will, by default, return an HTML-formatted representation of the resource when that resource is requested by a web browser. This allows for the API to return a fully web-browsable HTML representation.
+
+Having a web-browsable API is a huge usability win, and makes developing and using your API much easier. It also dramatically lowers the barrier-to-entry for other developers wanting to inspect and work with your API.
+
+See the [browsable api][browsable-api] topic for more information about the browsable API feature and how to customize it.
+
+## What's next?
+
+In [tutorial part 3][tut-3], we'll start using class-based views, and see how generic views reduce the amount of code we need to write.
+
+[json-url]: http://example.com/api/items/4.json
+[devserver]: http://127.0.0.1:8000/snippets/
+[browsable-api]: ../topics/browsable-api.md
+[tut-1]: 1-serialization.md
+[tut-3]: 3-class-based-views.md
diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md
new file mode 100644
index 000000000..ccfcd095d
--- /dev/null
+++ b/docs/tutorial/3-class-based-views.md
@@ -0,0 +1,150 @@
+# Tutorial 3: Class-based Views
+
+We can also write our API views using class-based views, rather than function based views. As we'll see this is a powerful pattern that allows us to reuse common functionality, and helps us keep our code [DRY][dry].
+
+## Rewriting our API using class-based views
+
+We'll start by rewriting the root view as a class-based view. All this involves is a little bit of refactoring of `views.py`.
+
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+ from django.http import Http404
+ from rest_framework.views import APIView
+ from rest_framework.response import Response
+ from rest_framework import status
+
+
+ class SnippetList(APIView):
+ """
+ List all snippets, or create a new snippet.
+ """
+ def get(self, request, format=None):
+ snippets = Snippet.objects.all()
+ serializer = SnippetSerializer(snippets, many=True)
+ return Response(serializer.data)
+
+ def post(self, request, format=None):
+ serializer = SnippetSerializer(data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+So far, so good. It looks pretty similar to the previous case, but we've got better separation between the different HTTP methods. We'll also need to update the instance view in `views.py`.
+
+ class SnippetDetail(APIView):
+ """
+ Retrieve, update or delete a snippet instance.
+ """
+ def get_object(self, pk):
+ try:
+ return Snippet.objects.get(pk=pk)
+ except Snippet.DoesNotExist:
+ raise Http404
+
+ def get(self, request, pk, format=None):
+ snippet = self.get_object(pk)
+ serializer = SnippetSerializer(snippet)
+ return Response(serializer.data)
+
+ def put(self, request, pk, format=None):
+ snippet = self.get_object(pk)
+ serializer = SnippetSerializer(snippet, data=request.data)
+ if serializer.is_valid():
+ serializer.save()
+ return Response(serializer.data)
+ return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
+
+ def delete(self, request, pk, format=None):
+ snippet = self.get_object(pk)
+ snippet.delete()
+ return Response(status=status.HTTP_204_NO_CONTENT)
+
+That's looking good. Again, it's still pretty similar to the function based view right now.
+
+We'll also need to refactor our `snippets/urls.py` slightly now that we're using class-based views.
+
+ from django.urls import path
+ from rest_framework.urlpatterns import format_suffix_patterns
+ from snippets import views
+
+ urlpatterns = [
+ path('snippets/', views.SnippetList.as_view()),
+ path('snippets//', views.SnippetDetail.as_view()),
+ ]
+
+ urlpatterns = format_suffix_patterns(urlpatterns)
+
+Okay, we're done. If you run the development server everything should be working just as before.
+
+## Using mixins
+
+One of the big wins of using class-based views is that it allows us to easily compose reusable bits of behavior.
+
+The create/retrieve/update/delete operations that we've been using so far are going to be pretty similar for any model-backed API views we create. Those bits of common behavior are implemented in REST framework's mixin classes.
+
+Let's take a look at how we can compose the views by using the mixin classes. Here's our `views.py` module again.
+
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+ from rest_framework import mixins
+ from rest_framework import generics
+
+ class SnippetList(mixins.ListModelMixin,
+ mixins.CreateModelMixin,
+ generics.GenericAPIView):
+ queryset = Snippet.objects.all()
+ serializer_class = SnippetSerializer
+
+ def get(self, request, *args, **kwargs):
+ return self.list(request, *args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+ return self.create(request, *args, **kwargs)
+
+We'll take a moment to examine exactly what's happening here. We're building our view using `GenericAPIView`, and adding in `ListModelMixin` and `CreateModelMixin`.
+
+The base class provides the core functionality, and the mixin classes provide the `.list()` and `.create()` actions. We're then explicitly binding the `get` and `post` methods to the appropriate actions. Simple enough stuff so far.
+
+ class SnippetDetail(mixins.RetrieveModelMixin,
+ mixins.UpdateModelMixin,
+ mixins.DestroyModelMixin,
+ generics.GenericAPIView):
+ queryset = Snippet.objects.all()
+ serializer_class = SnippetSerializer
+
+ def get(self, request, *args, **kwargs):
+ return self.retrieve(request, *args, **kwargs)
+
+ def put(self, request, *args, **kwargs):
+ return self.update(request, *args, **kwargs)
+
+ def delete(self, request, *args, **kwargs):
+ return self.destroy(request, *args, **kwargs)
+
+Pretty similar. Again we're using the `GenericAPIView` class to provide the core functionality, and adding in mixins to provide the `.retrieve()`, `.update()` and `.destroy()` actions.
+
+## Using generic class-based views
+
+Using the mixin classes we've rewritten the views to use slightly less code than before, but we can go one step further. REST framework provides a set of already mixed-in generic views that we can use to trim down our `views.py` module even more.
+
+ from snippets.models import Snippet
+ from snippets.serializers import SnippetSerializer
+ from rest_framework import generics
+
+
+ class SnippetList(generics.ListCreateAPIView):
+ queryset = Snippet.objects.all()
+ serializer_class = SnippetSerializer
+
+
+ class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
+ queryset = Snippet.objects.all()
+ serializer_class = SnippetSerializer
+
+Wow, that's pretty concise. We've gotten a huge amount for free, and our code looks like good, clean, idiomatic Django.
+
+Next we'll move onto [part 4 of the tutorial][tut-4], where we'll take a look at how we can deal with authentication and permissions for our API.
+
+[dry]: https://en.wikipedia.org/wiki/Don't_repeat_yourself
+[tut-4]: 4-authentication-and-permissions.md
diff --git a/docs/tutorial/4-authentication-and-permissions.md b/docs/tutorial/4-authentication-and-permissions.md
new file mode 100644
index 000000000..cb0321ea2
--- /dev/null
+++ b/docs/tutorial/4-authentication-and-permissions.md
@@ -0,0 +1,227 @@
+# Tutorial 4: Authentication & Permissions
+
+Currently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that:
+
+* Code snippets are always associated with a creator.
+* Only authenticated users may create snippets.
+* Only the creator of a snippet may update or delete it.
+* Unauthenticated requests should have full read-only access.
+
+## Adding information to our model
+
+We're going to make a couple of changes to our `Snippet` model class.
+First, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code.
+
+Add the following two fields to the `Snippet` model in `models.py`.
+
+ owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
+ highlighted = models.TextField()
+
+We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the `pygments` code highlighting library.
+
+We'll need some extra imports:
+
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters.html import HtmlFormatter
+ from pygments import highlight
+
+And now we can add a `.save()` method to our model class:
+
+ def save(self, *args, **kwargs):
+ """
+ Use the `pygments` library to create a highlighted HTML
+ representation of the code snippet.
+ """
+ lexer = get_lexer_by_name(self.language)
+ linenos = 'table' if self.linenos else False
+ options = {'title': self.title} if self.title else {}
+ formatter = HtmlFormatter(style=self.style, linenos=linenos,
+ full=True, **options)
+ self.highlighted = highlight(self.code, lexer, formatter)
+ super().save(*args, **kwargs)
+
+When that's all done we'll need to update our database tables.
+Normally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.
+
+ rm -f db.sqlite3
+ rm -r snippets/migrations
+ python manage.py makemigrations snippets
+ python manage.py migrate
+
+You might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the `createsuperuser` command.
+
+ python manage.py createsuperuser
+
+## Adding endpoints for our User models
+
+Now that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In `serializers.py` add:
+
+ from django.contrib.auth.models import User
+
+ class UserSerializer(serializers.ModelSerializer):
+ snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
+
+ class Meta:
+ model = User
+ fields = ['id', 'username', 'snippets']
+
+Because `'snippets'` is a *reverse* relationship on the User model, it will not be included by default when using the `ModelSerializer` class, so we needed to add an explicit field for it.
+
+We'll also add a couple of views to `views.py`. We'd like to just use read-only views for the user representations, so we'll use the `ListAPIView` and `RetrieveAPIView` generic class-based views.
+
+ from django.contrib.auth.models import User
+
+
+ class UserList(generics.ListAPIView):
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+
+
+ class UserDetail(generics.RetrieveAPIView):
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+
+Make sure to also import the `UserSerializer` class
+
+ from snippets.serializers import UserSerializer
+
+Finally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in `snippets/urls.py`.
+
+ path('users/', views.UserList.as_view()),
+ path('users//', views.UserDetail.as_view()),
+
+## Associating Snippets with Users
+
+Right now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request.
+
+The way we deal with that is by overriding a `.perform_create()` method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL.
+
+On the `SnippetList` view class, add the following method:
+
+ def perform_create(self, serializer):
+ serializer.save(owner=self.request.user)
+
+The `create()` method of our serializer will now be passed an additional `'owner'` field, along with the validated data from the request.
+
+## Updating our serializer
+
+Now that snippets are associated with the user that created them, let's update our `SnippetSerializer` to reflect that. Add the following field to the serializer definition in `serializers.py`:
+
+ owner = serializers.ReadOnlyField(source='owner.username')
+
+**Note**: Make sure you also add `'owner',` to the list of fields in the inner `Meta` class.
+
+This field is doing something quite interesting. The `source` argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language.
+
+The field we've added is the untyped `ReadOnlyField` class, in contrast to the other typed fields, such as `CharField`, `BooleanField` etc... The untyped `ReadOnlyField` is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used `CharField(read_only=True)` here.
+
+## Adding required permissions to views
+
+Now that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets.
+
+REST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is `IsAuthenticatedOrReadOnly`, which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access.
+
+First add the following import in the views module
+
+ from rest_framework import permissions
+
+Then, add the following property to **both** the `SnippetList` and `SnippetDetail` view classes.
+
+ permission_classes = [permissions.IsAuthenticatedOrReadOnly]
+
+## Adding login to the Browsable API
+
+If you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.
+
+We can add a login view for use with the browsable API, by editing the URLconf in our project-level `urls.py` file.
+
+Add the following import at the top of the file:
+
+ from django.urls import path, include
+
+And, at the end of the file, add a pattern to include the login and logout views for the browsable API.
+
+ urlpatterns += [
+ path('api-auth/', include('rest_framework.urls')),
+ ]
+
+The `'api-auth/'` part of pattern can actually be whatever URL you want to use.
+
+Now if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.
+
+Once you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.
+
+## Object level permissions
+
+Really we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it.
+
+To do that we're going to need to create a custom permission.
+
+In the snippets app, create a new file, `permissions.py`
+
+ from rest_framework import permissions
+
+
+ class IsOwnerOrReadOnly(permissions.BasePermission):
+ """
+ Custom permission to only allow owners of an object to edit it.
+ """
+
+ def has_object_permission(self, request, view, obj):
+ # Read permissions are allowed to any request,
+ # so we'll always allow GET, HEAD or OPTIONS requests.
+ if request.method in permissions.SAFE_METHODS:
+ return True
+
+ # Write permissions are only allowed to the owner of the snippet.
+ return obj.owner == request.user
+
+Now we can add that custom permission to our snippet instance endpoint, by editing the `permission_classes` property on the `SnippetDetail` view class:
+
+ permission_classes = [permissions.IsAuthenticatedOrReadOnly,
+ IsOwnerOrReadOnly]
+
+Make sure to also import the `IsOwnerOrReadOnly` class.
+
+ from snippets.permissions import IsOwnerOrReadOnly
+
+Now, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.
+
+## Authenticating with the API
+
+Because we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any [authentication classes][authentication], so the defaults are currently applied, which are `SessionAuthentication` and `BasicAuthentication`.
+
+When we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests.
+
+If we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request.
+
+If we try to create a snippet without authenticating, we'll get an error:
+
+ http POST http://127.0.0.1:8000/snippets/ code="print(123)"
+
+ {
+ "detail": "Authentication credentials were not provided."
+ }
+
+We can make a successful request by including the username and password of one of the users we created earlier.
+
+ http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code="print(789)"
+
+ {
+ "id": 1,
+ "owner": "admin",
+ "title": "foo",
+ "code": "print(789)",
+ "linenos": false,
+ "language": "python",
+ "style": "friendly"
+ }
+
+## Summary
+
+We've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created.
+
+In [part 5][tut-5] of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.
+
+[authentication]: ../api-guide/authentication.md
+[tut-5]: 5-relationships-and-hyperlinked-apis.md
diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
new file mode 100644
index 000000000..f999fdf50
--- /dev/null
+++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
@@ -0,0 +1,155 @@
+# Tutorial 5: Relationships & Hyperlinked APIs
+
+At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.
+
+## Creating an endpoint for the root of our API
+
+Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the `@api_view` decorator we introduced earlier. In your `snippets/views.py` add:
+
+ from rest_framework.decorators import api_view
+ from rest_framework.response import Response
+ from rest_framework.reverse import reverse
+
+
+ @api_view(['GET'])
+ def api_root(request, format=None):
+ return Response({
+ 'users': reverse('user-list', request=request, format=format),
+ 'snippets': reverse('snippet-list', request=request, format=format)
+ })
+
+Two things should be noticed here. First, we're using REST framework's `reverse` function in order to return fully-qualified URLs; second, URL patterns are identified by convenience names that we will declare later on in our `snippets/urls.py`.
+
+## Creating an endpoint for the highlighted snippets
+
+The other obvious thing that's still missing from our pastebin API is the code highlighting endpoints.
+
+Unlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint.
+
+The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.
+
+Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own `.get()` method. In your `snippets/views.py` add:
+
+ from rest_framework import renderers
+
+ class SnippetHighlight(generics.GenericAPIView):
+ queryset = Snippet.objects.all()
+ renderer_classes = [renderers.StaticHTMLRenderer]
+
+ def get(self, request, *args, **kwargs):
+ snippet = self.get_object()
+ return Response(snippet.highlighted)
+
+As usual we need to add the new views that we've created in to our URLconf.
+We'll add a url pattern for our new API root in `snippets/urls.py`:
+
+ path('', views.api_root),
+
+And then add a url pattern for the snippet highlights:
+
+ path('snippets//highlight/', views.SnippetHighlight.as_view()),
+
+## Hyperlinking our API
+
+Dealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship:
+
+* Using primary keys.
+* Using hyperlinking between entities.
+* Using a unique identifying slug field on the related entity.
+* Using the default string representation of the related entity.
+* Nesting the related entity inside the parent representation.
+* Some other custom representation.
+
+REST framework supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys.
+
+In this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend `HyperlinkedModelSerializer` instead of the existing `ModelSerializer`.
+
+The `HyperlinkedModelSerializer` has the following differences from `ModelSerializer`:
+
+* It does not include the `id` field by default.
+* It includes a `url` field, using `HyperlinkedIdentityField`.
+* Relationships use `HyperlinkedRelatedField`,
+ instead of `PrimaryKeyRelatedField`.
+
+We can easily re-write our existing serializers to use hyperlinking. In your `snippets/serializers.py` add:
+
+ class SnippetSerializer(serializers.HyperlinkedModelSerializer):
+ owner = serializers.ReadOnlyField(source='owner.username')
+ highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')
+
+ class Meta:
+ model = Snippet
+ fields = ['url', 'id', 'highlight', 'owner',
+ 'title', 'code', 'linenos', 'language', 'style']
+
+
+ class UserSerializer(serializers.HyperlinkedModelSerializer):
+ snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
+
+ class Meta:
+ model = User
+ fields = ['url', 'id', 'username', 'snippets']
+
+Notice that we've also added a new `'highlight'` field. This field is of the same type as the `url` field, except that it points to the `'snippet-highlight'` url pattern, instead of the `'snippet-detail'` url pattern.
+
+Because we've included format suffixed URLs such as `'.json'`, we also need to indicate on the `highlight` field that any format suffixed hyperlinks it returns should use the `'.html'` suffix.
+
+## Making sure our URL patterns are named
+
+If we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name.
+
+* The root of our API refers to `'user-list'` and `'snippet-list'`.
+* Our snippet serializer includes a field that refers to `'snippet-highlight'`.
+* Our user serializer includes a field that refers to `'snippet-detail'`.
+* Our snippet and user serializers include `'url'` fields that by default will refer to `'{model_name}-detail'`, which in this case will be `'snippet-detail'` and `'user-detail'`.
+
+After adding all those names into our URLconf, our final `snippets/urls.py` file should look like this:
+
+ from django.urls import path
+ from rest_framework.urlpatterns import format_suffix_patterns
+ from snippets import views
+
+ # API endpoints
+ urlpatterns = format_suffix_patterns([
+ path('', views.api_root),
+ path('snippets/',
+ views.SnippetList.as_view(),
+ name='snippet-list'),
+ path('snippets//',
+ views.SnippetDetail.as_view(),
+ name='snippet-detail'),
+ path('snippets//highlight/',
+ views.SnippetHighlight.as_view(),
+ name='snippet-highlight'),
+ path('users/',
+ views.UserList.as_view(),
+ name='user-list'),
+ path('users//',
+ views.UserDetail.as_view(),
+ name='user-detail')
+ ])
+
+## Adding pagination
+
+The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages.
+
+We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting:
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+ 'PAGE_SIZE': 10
+ }
+
+Note that settings in REST framework are all namespaced into a single dictionary setting, named `REST_FRAMEWORK`, which helps keep them well separated from your other project settings.
+
+We could also customize the pagination style if we needed to, but in this case we'll just stick with the default.
+
+## Browsing the API
+
+If we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links.
+
+You'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations.
+
+In [part 6][tut-6] of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.
+
+[tut-6]: 6-viewsets-and-routers.md
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
new file mode 100644
index 000000000..f9b6c5e9a
--- /dev/null
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -0,0 +1,136 @@
+# Tutorial 6: ViewSets & Routers
+
+REST framework includes an abstraction for dealing with `ViewSets`, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.
+
+`ViewSet` classes are almost the same thing as `View` classes, except that they provide operations such as `retrieve`, or `update`, and not method handlers such as `get` or `put`.
+
+A `ViewSet` class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a `Router` class which handles the complexities of defining the URL conf for you.
+
+## Refactoring to use ViewSets
+
+Let's take our current set of views, and refactor them into view sets.
+
+First of all let's refactor our `UserList` and `UserDetail` classes into a single `UserViewSet` class. We can remove the two view classes, and replace them with a single ViewSet class:
+
+ from rest_framework import viewsets
+
+
+ class UserViewSet(viewsets.ReadOnlyModelViewSet):
+ """
+ This viewset automatically provides `list` and `retrieve` actions.
+ """
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+
+Here we've used the `ReadOnlyModelViewSet` class to automatically provide the default 'read-only' operations. We're still setting the `queryset` and `serializer_class` attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.
+
+Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighlight` view classes. We can remove the three views, and again replace them with a single class.
+
+ from rest_framework import permissions
+ from rest_framework import renderers
+ from rest_framework.decorators import action
+ from rest_framework.response import Response
+
+
+ class SnippetViewSet(viewsets.ModelViewSet):
+ """
+ This ViewSet automatically provides `list`, `create`, `retrieve`,
+ `update` and `destroy` actions.
+
+ Additionally we also provide an extra `highlight` action.
+ """
+ queryset = Snippet.objects.all()
+ serializer_class = SnippetSerializer
+ permission_classes = [permissions.IsAuthenticatedOrReadOnly,
+ IsOwnerOrReadOnly]
+
+ @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
+ def highlight(self, request, *args, **kwargs):
+ snippet = self.get_object()
+ return Response(snippet.highlighted)
+
+ def perform_create(self, serializer):
+ serializer.save(owner=self.request.user)
+
+This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations.
+
+Notice that we've also used the `@action` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style.
+
+Custom actions which use the `@action` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests.
+
+The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include `url_path` as a decorator keyword argument.
+
+## Binding ViewSets to URLs explicitly
+
+The handler methods only get bound to the actions when we define the URLConf.
+To see what's going on under the hood let's first explicitly create a set of views from our ViewSets.
+
+In the `snippets/urls.py` file we bind our `ViewSet` classes into a set of concrete views.
+
+ from rest_framework import renderers
+
+ from snippets.views import api_root, SnippetViewSet, UserViewSet
+
+ snippet_list = SnippetViewSet.as_view({
+ 'get': 'list',
+ 'post': 'create'
+ })
+ snippet_detail = SnippetViewSet.as_view({
+ 'get': 'retrieve',
+ 'put': 'update',
+ 'patch': 'partial_update',
+ 'delete': 'destroy'
+ })
+ snippet_highlight = SnippetViewSet.as_view({
+ 'get': 'highlight'
+ }, renderer_classes=[renderers.StaticHTMLRenderer])
+ user_list = UserViewSet.as_view({
+ 'get': 'list'
+ })
+ user_detail = UserViewSet.as_view({
+ 'get': 'retrieve'
+ })
+
+Notice how we're creating multiple views from each `ViewSet` class, by binding the HTTP methods to the required action for each view.
+
+Now that we've bound our resources into concrete views, we can register the views with the URL conf as usual.
+
+ urlpatterns = format_suffix_patterns([
+ path('', api_root),
+ path('snippets/', snippet_list, name='snippet-list'),
+ path('snippets//', snippet_detail, name='snippet-detail'),
+ path('snippets//highlight/', snippet_highlight, name='snippet-highlight'),
+ path('users/', user_list, name='user-list'),
+ path('users//', user_detail, name='user-detail')
+ ])
+
+## Using Routers
+
+Because we're using `ViewSet` classes rather than `View` classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a `Router` class. All we need to do is register the appropriate view sets with a router, and let it do the rest.
+
+Here's our re-wired `snippets/urls.py` file.
+
+ from django.urls import path, include
+ from rest_framework.routers import DefaultRouter
+
+ from snippets import views
+
+ # Create a router and register our ViewSets with it.
+ router = DefaultRouter()
+ router.register(r'snippets', views.SnippetViewSet, basename='snippet')
+ router.register(r'users', views.UserViewSet, basename='user')
+
+ # The API URLs are now determined automatically by the router.
+ urlpatterns = [
+ path('', include(router.urls)),
+ ]
+
+Registering the ViewSets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the view set itself.
+
+The `DefaultRouter` class we're using also automatically creates the API root view for us, so we can now delete the `api_root` function from our `views` module.
+
+## Trade-offs between views vs ViewSets
+
+Using ViewSets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.
+
+That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function-based views. Using ViewSets is less explicit than building your API views individually.
diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
new file mode 100644
index 000000000..09f249f6e
--- /dev/null
+++ b/docs/tutorial/quickstart.md
@@ -0,0 +1,223 @@
+# Quickstart
+
+We're going to create a simple API to allow admin users to view and edit the users and groups in the system.
+
+## Project setup
+
+Create a new Django project named `tutorial`, then start a new app called `quickstart`.
+
+ # Create the project directory
+ mkdir tutorial
+ cd tutorial
+
+ # Create a virtual environment to isolate our package dependencies locally
+ python3 -m venv env
+ source env/bin/activate # On Windows use `env\Scripts\activate`
+
+ # Install Django and Django REST framework into the virtual environment
+ pip install django
+ pip install djangorestframework
+
+ # Set up a new project with a single application
+ django-admin startproject tutorial . # Note the trailing '.' character
+ cd tutorial
+ django-admin startapp quickstart
+ cd ..
+
+The project layout should look like:
+
+ $ pwd
+ /tutorial
+ $ find .
+ .
+ ./tutorial
+ ./tutorial/asgi.py
+ ./tutorial/__init__.py
+ ./tutorial/quickstart
+ ./tutorial/quickstart/migrations
+ ./tutorial/quickstart/migrations/__init__.py
+ ./tutorial/quickstart/models.py
+ ./tutorial/quickstart/__init__.py
+ ./tutorial/quickstart/apps.py
+ ./tutorial/quickstart/admin.py
+ ./tutorial/quickstart/tests.py
+ ./tutorial/quickstart/views.py
+ ./tutorial/settings.py
+ ./tutorial/urls.py
+ ./tutorial/wsgi.py
+ ./env
+ ./env/...
+ ./manage.py
+
+It may look unusual that the application has been created within the project directory. Using the project's namespace avoids name clashes with external modules (a topic that goes outside the scope of the quickstart).
+
+Now sync your database for the first time:
+
+ python manage.py migrate
+
+We'll also create an initial user named `admin` with a password. We'll authenticate as that user later in our example.
+
+ python manage.py createsuperuser --username admin --email admin@example.com
+
+Once you've set up a database and the initial user is created and ready to go, open up the app's directory and we'll get coding...
+
+## Serializers
+
+First up we're going to define some serializers. Let's create a new module named `tutorial/quickstart/serializers.py` that we'll use for our data representations.
+
+ from django.contrib.auth.models import Group, User
+ from rest_framework import serializers
+
+
+ class UserSerializer(serializers.HyperlinkedModelSerializer):
+ class Meta:
+ model = User
+ fields = ['url', 'username', 'email', 'groups']
+
+
+ class GroupSerializer(serializers.HyperlinkedModelSerializer):
+ class Meta:
+ model = Group
+ fields = ['url', 'name']
+
+Notice that we're using hyperlinked relations in this case with `HyperlinkedModelSerializer`. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.
+
+## Views
+
+Right, we'd better write some views then. Open `tutorial/quickstart/views.py` and get typing.
+
+ from django.contrib.auth.models import Group, User
+ from rest_framework import permissions, viewsets
+
+ from tutorial.quickstart.serializers import GroupSerializer, UserSerializer
+
+
+ class UserViewSet(viewsets.ModelViewSet):
+ """
+ API endpoint that allows users to be viewed or edited.
+ """
+ queryset = User.objects.all().order_by('-date_joined')
+ serializer_class = UserSerializer
+ permission_classes = [permissions.IsAuthenticated]
+
+
+ class GroupViewSet(viewsets.ModelViewSet):
+ """
+ API endpoint that allows groups to be viewed or edited.
+ """
+ queryset = Group.objects.all()
+ serializer_class = GroupSerializer
+ permission_classes = [permissions.IsAuthenticated]
+
+Rather than write multiple views we're grouping together all the common behavior into classes called `ViewSets`.
+
+We can easily break these down into individual views if we need to, but using viewsets keeps the view logic nicely organized as well as being very concise.
+
+## URLs
+
+Okay, now let's wire up the API URLs. On to `tutorial/urls.py`...
+
+ from django.urls import include, path
+ from rest_framework import routers
+
+ from tutorial.quickstart import views
+
+ router = routers.DefaultRouter()
+ router.register(r'users', views.UserViewSet)
+ router.register(r'groups', views.GroupViewSet)
+
+ # Wire up our API using automatic URL routing.
+ # Additionally, we include login URLs for the browsable API.
+ urlpatterns = [
+ path('', include(router.urls)),
+ path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
+ ]
+
+ urlpatterns += router.urls
+
+Because we're using viewsets instead of views, we can automatically generate the URL conf for our API, by simply registering the viewsets with a router class.
+
+Again, if we need more control over the API URLs we can simply drop down to using regular class-based views, and writing the URL conf explicitly.
+
+Finally, we're including default login and logout views for use with the browsable API. That's optional, but useful if your API requires authentication and you want to use the browsable API.
+
+## Pagination
+Pagination allows you to control how many objects per page are returned. To enable it add the following lines to `tutorial/settings.py`
+
+ REST_FRAMEWORK = {
+ 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
+ 'PAGE_SIZE': 10
+ }
+
+## Settings
+
+Add `'rest_framework'` to `INSTALLED_APPS`. The settings module will be in `tutorial/settings.py`
+
+ INSTALLED_APPS = [
+ ...
+ 'rest_framework',
+ ]
+
+Okay, we're done.
+
+---
+
+## Testing our API
+
+We're now ready to test the API we've built. Let's fire up the server from the command line.
+
+ python manage.py runserver
+
+We can now access our API, both from the command-line, using tools like `curl`...
+
+ bash: curl -u admin -H 'Accept: application/json; indent=4' http://127.0.0.1:8000/users/
+ Enter host password for user 'admin':
+ {
+ "count": 1,
+ "next": null,
+ "previous": null,
+ "results": [
+ {
+ "url": "http://127.0.0.1:8000/users/1/",
+ "username": "admin",
+ "email": "admin@example.com",
+ "groups": []
+ }
+ ]
+ }
+
+Or using the [httpie][httpie], command line tool...
+
+ bash: http -a admin http://127.0.0.1:8000/users/
+ http: password for admin@127.0.0.1:8000::
+ $HTTP/1.1 200 OK
+ ...
+ {
+ "count": 1,
+ "next": null,
+ "previous": null,
+ "results": [
+ {
+ "email": "admin@example.com",
+ "groups": [],
+ "url": "http://127.0.0.1:8000/users/1/",
+ "username": "admin"
+ }
+ ]
+ }
+
+
+Or directly through the browser, by going to the URL `http://127.0.0.1:8000/users/`...
+
+![Quick start image][image]
+
+If you're working through the browser, make sure to login using the control in the top right corner.
+
+Great, that was easy!
+
+If you want to get a more in depth understanding of how REST framework fits together head on over to [the tutorial][tutorial], or start browsing the [API guide][guide].
+
+[image]: ../img/quickstart.png
+[tutorial]: 1-serialization.md
+[guide]: ../api-guide/requests.md
+[httpie]: https://httpie.io/docs#installation
diff --git a/docs_theme/404.html b/docs_theme/404.html
new file mode 100644
index 000000000..bbb6b70ff
--- /dev/null
+++ b/docs_theme/404.html
@@ -0,0 +1,9 @@
+{% extends "main.html" %}
+
+{% block content %}
+
+