Commit 83ee0f11 by John Eskew

Merge tag '3.6.3' into jeskew/drf_3.6.3_plus_edx_patch

version 3.6.3
parents 41b796d8 5f386f58
......@@ -11,6 +11,7 @@
/*.egg-info/
/env/
MANIFEST
coverage.*
!.gitignore
!.travis.yml
......
......@@ -2,5 +2,6 @@
skip=.tox
atomic=true
multi_line_output=5
known_standard_library=types
known_third_party=pytest,django
known_first_party=rest_framework
language: python
python:
- "2.7"
- "3.4"
- "3.5"
sudo: false
env:
- TOX_ENV=py27-lint
- TOX_ENV=py27-docs
- TOX_ENV=py34-django18
- TOX_ENV=py33-django18
- TOX_ENV=py32-django18
- TOX_ENV=py27-django18
- TOX_ENV=py34-django17
- TOX_ENV=py33-django17
- TOX_ENV=py32-django17
- TOX_ENV=py27-django17
- TOX_ENV=py34-django16
- TOX_ENV=py33-django16
- TOX_ENV=py32-django16
- TOX_ENV=py27-django16
- TOX_ENV=py26-django16
- TOX_ENV=py34-django15
- TOX_ENV=py33-django15
- TOX_ENV=py32-django15
- TOX_ENV=py27-django15
- TOX_ENV=py26-django15
- TOX_ENV=py27-djangomaster
- TOX_ENV=py32-djangomaster
- TOX_ENV=py33-djangomaster
- TOX_ENV=py34-djangomaster
- DJANGO=1.8
- DJANGO=1.9
- DJANGO=1.10
- DJANGO=1.11
- DJANGO=master
matrix:
fast_finish: true
allow_failures:
- env: TOX_ENV=py27-djangomaster
- env: TOX_ENV=py32-djangomaster
- env: TOX_ENV=py33-djangomaster
- env: TOX_ENV=py34-djangomaster
fast_finish: true
include:
- python: "3.6"
env: DJANGO=master
- python: "3.6"
env: DJANGO=1.11
- python: "3.3"
env: DJANGO=1.8
- python: "2.7"
env: TOXENV="lint"
- python: "2.7"
env: TOXENV="docs"
exclude:
- python: "2.7"
env: DJANGO=master
- python: "3.4"
env: DJANGO=master
allow_failures:
- env: DJANGO=master
install:
- pip install tox
- pip install tox tox-travis
script:
- tox -e $TOX_ENV
- tox
after_success:
- pip install codecov
- codecov
- codecov -e TOXENV,DJANGO
notifications:
email: false
[main]
host = https://www.transifex.com
lang_map = sr@latin:sr_Latn, zh-Hans:zh_Hans, zh-Hant:zh_Hant
[django-rest-framework.djangopo]
file_filter = rest_framework/locale/<lang>/LC_MESSAGES/django.po
......
......@@ -33,7 +33,7 @@ Some tips on good issue reporting:
* When describing issues try to phrase your ticket in terms of the *behavior* you think needs changing rather than the *code* you think need changing.
* Search the issue list first for related items, and make sure you're running the latest version of REST framework before reporting an issue.
* If reporting a bug, then try to include a pull request with a failing test case. This will help us quickly identify if there is a valid issue, and make sure that it gets fixed more quickly if there is one.
* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bugfixes, and great documentation.
* Feature requests will often be closed with a recommendation that they be implemented outside of the core REST framework library. Keeping new feature requests implemented as third party libraries allows us to keep down the maintenance overhead of REST framework, so that the focus can be on continued stability, bug fixes, and great documentation.
* Closing an issue doesn't necessarily mean the end of a discussion. If you believe your issue has been closed incorrectly, explain why and we'll consider if it needs to be reopened.
## Triaging issues
......@@ -50,7 +50,7 @@ Getting involved in triaging incoming issues is a good way to start contributing
To start developing on Django REST framework, clone the repo:
git clone git@github.com:tomchristie/django-rest-framework.git
git clone git@github.com:encode/django-rest-framework.git
Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles.
......@@ -61,6 +61,7 @@ To run the tests, clone the repository, and then:
# Setup the virtual environment
virtualenv env
source env/bin/activate
pip install django
pip install -r requirements.txt
# Run the tests
......@@ -118,10 +119,6 @@ Always run the tests before submitting pull requests, and ideally run `tox` in o
Once you've made a pull request take a look at the Travis build status in the GitHub interface and make sure the tests are running as you'd expect.
![Travis status][travis-status]
*Above: Travis build notifications*
## Managing compatibility issues
Sometimes, in order to ensure your code works on various different versions of Django, Python or third party libraries, you'll need to run slightly different code depending on the environment. Any code that branches in this way should be isolated into the `compat.py` module, and should provide a single common interface that the rest of the codebase can use.
......@@ -201,11 +198,10 @@ If you want to draw attention to a note or warning, use a pair of enclosing line
[code-of-conduct]: https://www.djangoproject.com/conduct/
[google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[so-filter]: http://stackexchange.com/filters/66475/rest-framework
[issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open
[issues]: https://github.com/encode/django-rest-framework/issues?state=open
[pep-8]: http://www.python.org/dev/peps/pep-0008/
[travis-status]: ../img/travis-status.png
[pull-requests]: https://help.github.com/articles/using-pull-requests
[tox]: http://tox.readthedocs.org/en/latest/
[tox]: https://tox.readthedocs.io/en/latest/
[markdown]: http://daringfireball.net/projects/markdown/basics
[docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs
[docs]: https://github.com/encode/django-rest-framework/tree/master/docs
[mou]: http://mouapp.com/
## Checklist
- [ ] I have verified that that issue exists against the `master` branch of Django REST framework.
- [ ] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [ ] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [ ] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [ ] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)
## Steps to reproduce
## Expected behavior
## Actual behavior
# License
Copyright (c) 2011-2015, Tom Christie
Copyright (c) 2011-2017, Tom Christie
All rights reserved.
Redistribution and use in source and binary forms, with or without
......
*Note*: Before submitting this pull request, please review our [contributing guidelines](https://github.com/encode/django-rest-framework/blob/master/CONTRIBUTING.md#pull-requests).
## Description
Please describe your pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. When linking to an issue, please use `refs #...` in the description of the pull request.
......@@ -3,6 +3,7 @@
[![build-status-image]][travis]
[![coverage-status-image]][codecov]
[![pypi-version]][pypi]
[![Gitter](https://badges.gitter.im/tomchristie/django-rest-framework.svg)](https://gitter.im/tomchristie/django-rest-framework?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
**Awesome web-browsable Web APIs.**
......@@ -10,9 +11,25 @@ Full documentation for the project is available at [http://www.django-rest-frame
---
**Note**: We have now released Django REST framework 3.2. For older codebases you may want to refer to the version 2.4.4 [source code][2.4-code], and [documentation][2.4-docs].
# Funding
For more details see the 3.2 [announcement][3.2-announcement] and [release notes][3.2-release-notes].
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]**.
The initial aim is to provide a single full-time position on REST framework.
*Every single sign-up makes a significant impact towards making that possible.*
<p align="center">
<a href="http://jobs.rover.com/"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rover-readme.png"/></a>
<a href="https://getsentry.com/welcome/"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/sentry-readme.png"/></a>
<a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/stream-readme.png"/></a>
<a href="https://hello.machinalis.co.uk/"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/machinalis-readme.png"/></a>
<a href="https://rollbar.com/"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/rollbar-readme.png"/></a>
<a href="https://micropyramid.com/django-rest-framework-development-services/"><img src="https://raw.githubusercontent.com/encode/django-rest-framework/master/docs/img/premium/micropyramid-readme.png"/></a>
</p>
*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), [Rollbar](https://rollbar.com), and [MicroPyramid](https://micropyramid.com/django-rest-framework-development-services/).*
---
......@@ -36,8 +53,8 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements
* Python (2.6.5+, 2.7, 3.2, 3.3, 3.4)
* Django (1.5.6+, 1.6.3+, 1.7, 1.8)
* Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)
* Django (1.8, 1.9, 1.10, 1.11)
# Installation
......@@ -61,7 +78,9 @@ Startup up a new project like so...
pip install django
pip install djangorestframework
django-admin.py startproject example .
./manage.py syncdb
./manage.py migrate
./manage.py createsuperuser
Now edit the `example/urls.py` module in your project:
......@@ -153,20 +172,23 @@ You may also want to [follow the author on Twitter][twitter].
# Security
If you believe youve found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**.
If you believe you've found something in Django REST framework which has security implications, please **do not raise the issue in a public forum**.
Send a description of the issue via email to [rest-framework-security@googlegroups.com][security-mail]. The project maintainers will then work with you to resolve any issues where required, prior to any public disclosure.
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=master
[coverage-status-image]: https://img.shields.io/codecov/c/github/tomchristie/django-rest-framework/master.svg
[codecov]: http://codecov.io/github/tomchristie/django-rest-framework?branch=master
[build-status-image]: https://secure.travis-ci.org/encode/django-rest-framework.svg?branch=master
[travis]: http://travis-ci.org/encode/django-rest-framework?branch=master
[coverage-status-image]: https://img.shields.io/codecov/c/github/encode/django-rest-framework/master.svg
[codecov]: http://codecov.io/github/encode/django-rest-framework?branch=master
[pypi-version]: https://img.shields.io/pypi/v/djangorestframework.svg
[pypi]: https://pypi.python.org/pypi/djangorestframework
[twitter]: https://twitter.com/_tomchristie
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[sandbox]: http://restframework.herokuapp.com/
[funding]: https://fund.django-rest-framework.org/topics/funding/
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[oauth1-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth
[oauth2-section]: http://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit
[serializer-section]: http://www.django-rest-framework.org/api-guide/serializers/#serializers
......@@ -181,7 +203,3 @@ Send a description of the issue via email to [rest-framework-security@googlegrou
[docs]: http://www.django-rest-framework.org/
[security-mail]: mailto:rest-framework-security@googlegroups.com
[2.4-code]: https://github.com/tomchristie/django-rest-framework/tree/version-2.4.x
[2.4-docs]: http://tomchristie.github.io/rest-framework-2-docs/
[3.2-announcement]: http://www.django-rest-framework.org/topics/3.2-announcement/
[3.2-release-notes]: http://www.django-rest-framework.org/topics/release-notes/#32x-series
coverage:
status:
project: false
patch: false
changes: false
comment: off
......@@ -44,7 +44,7 @@ The default authentication schemes may be set globally, using the `DEFAULT_AUTHE
}
You can also set the authentication scheme on a per-view or per-viewset basis,
using the `APIView` class based views.
using the `APIView` class-based views.
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
......@@ -128,11 +128,10 @@ To use the `TokenAuthentication` scheme you'll need to [configure the authentica
---
**Note:** Make sure to run `manage.py syncdb` after changing your settings. The `rest_framework.authtoken` app provides both Django (from v1.7) and South database migrations. See [Schema migrations](#schema-migrations) below.
**Note:** Make sure to run `manage.py migrate` after changing your settings. The `rest_framework.authtoken` app provides Django database migrations.
---
You'll also need to create tokens for your users.
from rest_framework.authtoken.models import Token
......@@ -144,10 +143,12 @@ For clients to authenticate, the token key should be included in the `Authorizat
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
**Note:** If you want to use a different keyword in the header, such as `Bearer`, simply subclass `TokenAuthentication` and set the `keyword` class variable.
If successfully authenticated, `TokenAuthentication` provides the following credentials.
* `request.user` will be a Django `User` instance.
* `request.auth` will be a `rest_framework.authtoken.models.BasicToken` instance.
* `request.auth` will be a `rest_framework.authtoken.models.Token` instance.
Unauthenticated responses that are denied permission will result in an `HTTP 401 Unauthorized` response with an appropriate WWW-Authenticate header. For example:
......@@ -165,6 +166,8 @@ The `curl` command line tool may be useful for testing token authenticated APIs.
#### Generating Tokens
##### By using signals
If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal.
from django.conf import settings
......@@ -187,6 +190,8 @@ If you've already created some users, you can generate tokens for all existing u
for user in User.objects.all():
Token.objects.get_or_create(user=user)
##### By exposing an api endpoint
When using `TokenAuthentication`, you may want to provide a mechanism for clients to obtain a token given the username and password. REST framework provides a built-in view to provide this behavior. To use it, add the `obtain_auth_token` view to your URLconf:
from rest_framework.authtoken import views
......@@ -202,37 +207,20 @@ The `obtain_auth_token` view will return a JSON response when valid `username` a
Note that the default `obtain_auth_token` view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the `obtain_auth_token` view, you can do so by overriding the `ObtainAuthToken` view class, and using that in your url conf instead.
#### Schema migrations
By default there are no permissions or throttling applied to the `obtain_auth_token` view. If you do wish to apply throttling you'll need to override the view class,
and include them using the `throttle_classes` attribute.
The `rest_framework.authtoken` app includes both Django native migrations (for Django versions >1.7) and South migrations (for Django versions <1.7) that will create the authtoken table.
----
##### With Django admin
**Note**: From REST Framework v2.4.0 using South with Django <1.7 requires upgrading South v1.0+
It is also possible to create Tokens manually through admin interface. In case you are using a large user base, we recommend that you monkey patch the `TokenAdmin` class to customize it to your needs, more specifically by declaring the `user` field as `raw_field`.
----
`your_app/admin.py`:
from rest_framework.authtoken.admin import TokenAdmin
If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created.
You can do so by inserting a `needed_by` attribute in your user migration:
class Migration:
needed_by = (
('authtoken', '0001_initial'),
)
TokenAdmin.raw_id_fields = ('user',)
def forwards(self):
...
For more details, see the [south documentation on dependencies][south-dependencies].
Also note that if you're using a `post_save` signal to create tokens, then the first time you create the database tables, you'll need to ensure any migrations are run prior to creating any superusers. For example:
python manage.py syncdb --noinput # Won't create a superuser just yet, due to `--noinput`.
python manage.py migrate
python manage.py createsuperuser
## SessionAuthentication
......@@ -360,10 +348,18 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[Django-rest-auth][django-rest-auth] library provides a set of REST API endpoints for registration, authentication (including social media authentication), password reset, retrieve and update user details, etc. By having these API endpoints, your client apps such as AngularJS, iOS, Android, and others can communicate to your Django backend site independently via REST APIs for user management.
## django-rest-framework-social-oauth2
[Django-rest-framework-social-oauth2][django-rest-framework-social-oauth2] library provides an easy way to integrate social plugins (facebook, twitter, google, etc.) to your authentication system and an easy oauth2 setup. With this library, you will be able to authenticate users based on external tokens (e.g. facebook access token), convert these tokens to "in-house" oauth2 tokens and use and generate oauth2 tokens to authenticate your users.
## django-rest-knox
[Django-rest-knox][django-rest-knox] library provides models and views to handle token based authentication in a more secure and extensible way than the built-in TokenAuthentication scheme - with Single Page Applications and Mobile clients in mind. It provides per-client tokens, and views to generate them when provided some other authentication (usually basic authentication), to delete the token (providing a server enforced logout) and to delete all tokens (logs out all clients that a user is logged into).
## drfpasswordless
[drfpasswordless][drfpasswordless] adds (Medium, Square Cash inspired) passwordless support to Django REST Framework's own TokenAuthentication scheme. Users log in and sign up with a token sent to a contact point like an email address or a mobile number.
[cite]: http://jacobian.org/writing/rest-worst-practices/
[http401]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
[http403]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4
......@@ -371,11 +367,9 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[oauth]: http://oauth.net/2/
[permission]: permissions.md
[throttling]: throttling.md
[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
[mod_wsgi_official]: http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization
[custom-user-model]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#specifying-a-custom-user-model
[south-dependencies]: http://south.readthedocs.org/en/latest/dependencies.html
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.org/en/latest/rest-framework/getting_started.html
[django-oauth-toolkit-getting-started]: https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html
[django-rest-framework-oauth]: http://jpadilla.github.io/django-rest-framework-oauth/
[django-rest-framework-oauth-authentication]: http://jpadilla.github.io/django-rest-framework-oauth/authentication/
[django-rest-framework-oauth-permissions]: http://jpadilla.github.io/django-rest-framework-oauth/permissions/
......@@ -384,7 +378,7 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[oauth-1.0a]: http://oauth.net/core/1.0a
[django-oauth-plus]: http://code.larlet.fr/django-oauth-plus
[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider
[django-oauth2-provider-docs]: https://django-oauth2-provider.readthedocs.org/en/latest/
[django-oauth2-provider-docs]: https://django-oauth2-provider.readthedocs.io/en/latest/
[rfc6749]: http://tools.ietf.org/html/rfc6749
[django-oauth-toolkit]: https://github.com/evonove/django-oauth-toolkit
[evonove]: https://github.com/evonove/
......@@ -398,10 +392,12 @@ HTTP Signature (currently a [IETF draft][http-signature-ietf-draft]) provides a
[djangorestframework-httpsignature]: https://github.com/etoccalino/django-rest-framework-httpsignature
[amazon-http-signature]: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
[http-signature-ietf-draft]: https://datatracker.ietf.org/doc/draft-cavage-http-signatures/
[hawkrest]: http://hawkrest.readthedocs.org/en/latest/
[hawkrest]: https://hawkrest.readthedocs.io/en/latest/
[hawk]: https://github.com/hueniverse/hawk
[mohawk]: http://mohawk.readthedocs.org/en/latest/
[mohawk]: https://mohawk.readthedocs.io/en/latest/
[mac]: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05
[djoser]: https://github.com/sunscrapers/djoser
[django-rest-auth]: https://github.com/Tivix/django-rest-auth
[django-rest-framework-social-oauth2]: https://github.com/PhilipGarnero/django-rest-framework-social-oauth2
[django-rest-knox]: https://github.com/James1345/django-rest-knox
[drfpasswordless]: https://github.com/aaronn/django-rest-framework-passwordless
......@@ -77,7 +77,7 @@ The default content negotiation class may be set globally, using the `DEFAULT_CO
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation',
}
You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class based views.
You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class-based views.
from myapp.negotiation import IgnoreClientContentNegotiation
from rest_framework.response import Response
......
......@@ -47,7 +47,7 @@ Any example validation error might look like this:
You can implement custom exception handling by creating a handler function that converts exceptions raised in your API views into response objects. This allows you to control the style of error responses used by your API.
The function must take a pair of arguments, this first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.
The function must take a pair of arguments, the first is the exception to be handled, and the second is a dictionary containing any extra context such as the view currently being handled. The exception handler function should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.
For example, you might want to ensure that all error responses include the HTTP status code in the body of the response, like so:
......@@ -98,7 +98,7 @@ Note that the exception handler will only be called for responses generated by r
The **base class** for all exceptions raised inside an `APIView` class or `@api_view`.
To provide a custom exception, subclass `APIException` and set the `.status_code` and `.default_detail` properties on the class.
To provide a custom exception, subclass `APIException` and set the `.status_code`, `.default_detail`, and `default_code` attributes on the class.
For example, if your API relies on a third party service that may sometimes be unreachable, you might want to implement an exception for the "503 Service Unavailable" HTTP response code. You could do this like so:
......@@ -107,10 +107,42 @@ For example, if your API relies on a third party service that may sometimes be u
class ServiceUnavailable(APIException):
status_code = 503
default_detail = 'Service temporarily unavailable, try again later.'
default_code = 'service_unavailable'
#### Inspecting API exceptions
There are a number of different properties available for inspecting the status
of an API exception. You can use these to build custom exception handling
for your project.
The available attributes and methods are:
* `.detail` - Return the textual description of the error.
* `.get_codes()` - Return the code identifier of the error.
* `.get_full_details()` - Return both the textual description and the code identifier.
In most cases the error detail will be a simple item:
>>> print(exc.detail)
You do not have permission to perform this action.
>>> print(exc.get_codes())
permission_denied
>>> print(exc.get_full_details())
{'message':'You do not have permission to perform this action.','code':'permission_denied'}
In the case of validation errors the error detail will be either a list or
dictionary of items:
>>> print(exc.detail)
{"name":"This field is required.","age":"A valid integer is required."}
>>> print(exc.get_codes())
{"name":"required","age":"invalid"}
>>> print(exc.get_full_details())
{"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}}
## ParseError
**Signature:** `ParseError(detail=None)`
**Signature:** `ParseError(detail=None, code=None)`
Raised if the request contains malformed data when accessing `request.data`.
......@@ -118,7 +150,7 @@ By default this exception results in a response with the HTTP status code "400 B
## AuthenticationFailed
**Signature:** `AuthenticationFailed(detail=None)`
**Signature:** `AuthenticationFailed(detail=None, code=None)`
Raised when an incoming request includes incorrect authentication.
......@@ -126,7 +158,7 @@ By default this exception results in a response with the HTTP status code "401 U
## NotAuthenticated
**Signature:** `NotAuthenticated(detail=None)`
**Signature:** `NotAuthenticated(detail=None, code=None)`
Raised when an unauthenticated request fails the permission checks.
......@@ -134,7 +166,7 @@ By default this exception results in a response with the HTTP status code "401 U
## PermissionDenied
**Signature:** `PermissionDenied(detail=None)`
**Signature:** `PermissionDenied(detail=None, code=None)`
Raised when an authenticated request fails the permission checks.
......@@ -142,7 +174,7 @@ By default this exception results in a response with the HTTP status code "403 F
## NotFound
**Signature:** `NotFound(detail=None)`
**Signature:** `NotFound(detail=None, code=None)`
Raised when a resource does not exists at the given URL. This exception is equivalent to the standard `Http404` Django exception.
......@@ -150,7 +182,7 @@ By default this exception results in a response with the HTTP status code "404 N
## MethodNotAllowed
**Signature:** `MethodNotAllowed(method, detail=None)`
**Signature:** `MethodNotAllowed(method, detail=None, code=None)`
Raised when an incoming request occurs that does not map to a handler method on the view.
......@@ -158,7 +190,7 @@ By default this exception results in a response with the HTTP status code "405 M
## NotAcceptable
**Signature:** `NotAcceptable(detail=None)`
**Signature:** `NotAcceptable(detail=None, code=None)`
Raised when an incoming request occurs with an `Accept` header that cannot be satisfied by any of the available renderers.
......@@ -166,7 +198,7 @@ By default this exception results in a response with the HTTP status code "406 N
## UnsupportedMediaType
**Signature:** `UnsupportedMediaType(media_type, detail=None)`
**Signature:** `UnsupportedMediaType(media_type, detail=None, code=None)`
Raised if there are no parsers that can handle the content type of the request data when accessing `request.data`.
......@@ -174,7 +206,7 @@ By default this exception results in a response with the HTTP status code "415 U
## Throttled
**Signature:** `Throttled(wait=None, detail=None)`
**Signature:** `Throttled(wait=None, detail=None, code=None)`
Raised when an incoming request fails the throttling checks.
......@@ -182,7 +214,7 @@ By default this exception results in a response with the HTTP status code "429 T
## ValidationError
**Signature:** `ValidationError(detail)`
**Signature:** `ValidationError(detail, code=None)`
The `ValidationError` exception is slightly different from the other `APIException` classes:
......
......@@ -42,7 +42,7 @@ When using `format_suffix_patterns`, you must make sure to add the `'format'` ke
def comment_list(request, format=None):
# do stuff...
Or with class based views:
Or with class-based views:
class CommentList(APIView):
def get(self, request, format=None):
......@@ -69,6 +69,16 @@ If using the `i18n_patterns` function provided by Django, as well as `format_suf
---
## Query parameter formats
An alternative to the format suffixes is to include the requested format in a query parameter. REST framework provides this option by default, and it is used in the browsable API to switch between differing available representations.
To select a representation using its short format, use the `format` query parameter. For example: `http://example.com/organizations/?format=csv`.
The name of this query parameter can be modified using the `URL_FORMAT_OVERRIDE` setting. Set the value to `None` to disable this behavior.
---
## Accept headers vs. format suffixes
There seems to be a view among some of the Web community that filename extensions are not a RESTful pattern, and that `HTTP Accept` headers should always be used instead.
......
......@@ -7,7 +7,7 @@ source: mixins.py
>
> &mdash; [Django Documentation][cite]
One of the key benefits of class based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns.
One of the key benefits of class-based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns.
The generic views provided by REST framework allow you to quickly build API views that map closely to your database models.
......@@ -26,7 +26,6 @@ Typically when using the generic views, you'll override the view, and set severa
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsAdminUser,)
paginate_by = 100
For more complex cases you might also want to override various methods on the view class. For example.
......@@ -35,14 +34,6 @@ For more complex cases you might also want to override various methods on the vi
serializer_class = UserSerializer
permission_classes = (IsAdminUser,)
def get_paginate_by(self):
"""
Use smaller pagination for HTML representations.
"""
if self.request.accepted_renderer.format == 'html':
return 20
return 100
def list(self, request):
# Note the use of `get_queryset()` instead of `self.queryset`
queryset = self.get_queryset()
......@@ -78,9 +69,7 @@ The following attributes control the basic view behavior.
The following attributes are used to control pagination when used with list views.
* `pagination_class` - The pagination class that should be used when paginating list results. Defaults to the same value as the `DEFAULT_PAGINATION_CLASS` setting, which is `'rest_framework.pagination.PageNumberPagination'`.
Note that usage of the `paginate_by`, `paginate_by_param` and `page_kwarg` attributes are now pending deprecation. The `pagination_serializer_class` attribute and `DEFAULT_PAGINATION_SERIALIZER_CLASS` setting have been removed completely. Pagination settings should instead be controlled by overriding a pagination class and setting any configuration attributes there. See the pagination documentation for more details.
* `pagination_class` - The pagination class that should be used when paginating list results. Defaults to the same value as the `DEFAULT_PAGINATION_CLASS` setting, which is `'rest_framework.pagination.PageNumberPagination'`. Setting `pagination_class=None` will disable pagination on this view.
**Filtering**:
......@@ -125,22 +114,22 @@ For example:
Note that if your API doesn't include any object level permissions, you may optionally exclude the `self.check_object_permissions`, and simply return the object from the `get_object_or_404` lookup.
#### `filter_queryset(self, queryset)`
Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.
For example:
def filter_queryset(self, queryset):
filter_backends = (CategoryFilter,)
if 'geo_route' in self.request.query_params:
filter_backends = (GeoRouteFilter, CategoryFilter)
elif 'geo_point' in self.request.query_params:
filter_backends = (GeoPointFilter, CategoryFilter)
for backend in list(filter_backends):
queryset = backend().filter_queryset(self.request, queryset, view=self)
return queryset
#### `get_serializer_class(self)`
......@@ -156,19 +145,6 @@ For example:
return FullAccountSerializer
return BasicAccountSerializer
#### `get_paginate_by(self)`
Returns the page size to use with pagination. By default this uses the `paginate_by` attribute, and may be overridden by the client if the `paginate_by_param` attribute is set.
You may want to override this method to provide more complex behavior, such as modifying page sizes based on the media type of the response.
For example:
def get_paginate_by(self):
if self.request.accepted_renderer.format == 'html':
return 20
return 100
**Save and deletion hooks**:
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
......@@ -244,8 +220,6 @@ Also provides a `.partial_update(request, *args, **kwargs)` method, which is sim
If an object is updated this returns a `200 OK` response, with a serialized representation of the object as the body of the response.
If an object is created, for example when making a `DELETE` request followed by a `PUT` request to the same URL, this returns a `201 Created` response, with a serialized representation of the object as the body of the response.
If the request data provided for updating the object was invalid, a `400 Bad Request` response will be returned, with the error details as the body of the response.
## DestroyModelMixin
......@@ -354,7 +328,8 @@ For example, if you need to lookup objects based on multiple fields in the URL c
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
filter[field] = self.kwargs[field]
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
return get_object_or_404(queryset, **filter) # Lookup the object
You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior.
......@@ -407,7 +382,7 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements
[Django Rest Multiple Models][django-rest-multiple-models] provides a generic view (and mixin) for sending multiple serialized models and/or querysets via a single API request.
[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views
[cite]: https://docs.djangoproject.com/en/stable/ref/class-based-views/#base-vs-generic-views
[GenericAPIView]: #genericapiview
[ListModelMixin]: #listmodelmixin
[CreateModelMixin]: #createmodelmixin
......@@ -416,5 +391,3 @@ The [django-rest-framework-bulk package][django-rest-framework-bulk] implements
[DestroyModelMixin]: #destroymodelmixin
[django-rest-framework-bulk]: https://github.com/miki725/django-rest-framework-bulk
[django-rest-multiple-models]: https://github.com/Axiologue/DjangoRestMultipleModels
......@@ -104,6 +104,18 @@ Then configure your settings to use this custom class:
'DEFAULT_METADATA_CLASS': 'myproject.apps.core.MinimalMetadata'
}
# Third party packages
The following third party packages provide additional metadata implementations.
## DRF-schema-adapter
[drf-schema-adapter][drf-schema-adapter] is a set of tools that makes it easier to provide schema information to frontend frameworks and libraries. It provides a metadata mixin as well as 2 metadata classes and several adapters suitable to generate [json-schema][json-schema] as well as schema information readable by various libraries.
You can also write your own adapter to work with your specific frontend.
If you wish to do so, it also provides an exporter that can export those schema information to json files.
[cite]: http://tools.ietf.org/html/rfc7231#section-4.3.7
[no-options]: https://www.mnot.net/blog/2012/10/29/NO_OPTIONS
[json-schema]: http://json-schema.org/
[drf-schema-adapter]: https://github.com/drf-forms/drf-schema-adapter
......@@ -17,14 +17,19 @@ The built-in styles currently all use links included as part of the content of t
Pagination is only performed automatically if you're using the generic views or viewsets. If you're using a regular `APIView`, you'll need to call into the pagination API yourself to ensure you return a paginated response. See the source code for the `mixins.ListModelMixin` and `generics.GenericAPIView` classes for an example.
Pagination can be turned off by setting the pagination class to `None`.
## Setting the pagination style
The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example, to use the built-in limit/offset pagination, you would do:
The default pagination style may be set globally, using the `DEFAULT_PAGINATION_CLASS` and `PAGE_SIZE` setting keys. For example, to use the built-in limit/offset pagination, you would do something like this:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
Note that you need to set both the pagination class, and the page size that should be used.
You can also set the pagination class on an individual view by using the `pagination_class` attribute. Typically you'll want to use the same pagination style throughout your API, although you might want to vary individual aspects of the pagination, such as default or maximum page size, on a per-view basis.
## Modifying the pagination style
......@@ -45,14 +50,14 @@ You can then apply your new style to a view using the `.pagination_class` attrib
class BillingRecordsView(generics.ListAPIView):
queryset = Billing.objects.all()
serializer = BillingRecordsSerializer
serializer_class = BillingRecordsSerializer
pagination_class = LargeResultsSetPagination
Or apply the style globally, using the `DEFAULT_PAGINATION_CLASS` settings key. For example:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}
}
---
......@@ -95,6 +100,7 @@ The `PageNumberPagination` class includes a number of attributes that may be ove
To set these attributes you should override the `PageNumberPagination` class, and then enable your custom pagination class as above.
* `django_paginator_class` - The Django Paginator class to use. Default is `django.core.paginator.Paginator`, which should be fine for most use cases.
* `page_size` - A numeric value indicating the page size. If set, this overrides the `PAGE_SIZE` setting. Defaults to the same value as the `PAGE_SIZE` settings key.
* `page_query_param` - A string value indicating the name of the query parameter to use for the pagination control.
* `page_size_query_param` - If set, this is a string value indicating the name of a query parameter that allows the client to set the page size on a per-request basis. Defaults to `None`, indicating that the client may not control the requested page size.
......@@ -106,7 +112,7 @@ To set these attributes you should override the `PageNumberPagination` class, an
## LimitOffsetPagination
This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an
This pagination style mirrors the syntax used when looking up multiple database records. The client includes both a "limit" and an
"offset" query parameter. The limit indicates the maximum number of items to return, and is equivalent to the `page_size` in other styles. The offset indicates the starting position of the query in relation to the complete set of unpaginated items.
**Request**:
......@@ -175,7 +181,7 @@ Proper usage of cursor pagination should have an ordering field that satisfies t
* Should be a non-nullable value that can be coerced to a string.
* The field should have a database index.
Using an ordering field that does not satisfy these constraints will generally still work, but you'll be loosing some of the benefits of cursor pagination.
Using an ordering field that does not satisfy these constraints will generally still work, but you'll be losing some of the benefits of cursor pagination.
For more technical details on the implementation we use for cursor pagination, the ["Building cursors for the Disqus API"][disqus-cursor-api] blog post gives a good overview of the basic approach.
......@@ -270,6 +276,15 @@ To have your custom pagination class be used by default, use the `DEFAULT_PAGINA
API responses for list endpoints will now include a `Link` header, instead of including the pagination links as part of the body of the response, for example:
## Pagination & schemas
You can also make the pagination controls available to the schema autogeneration
that REST framework provides, by implementing a `get_schema_fields()` method. This method should have the following signature:
`get_schema_fields(self, view)`
The method should return a list of `coreapi.Field` instances.
---
![Link Header][link-header]
......@@ -309,9 +324,14 @@ The following third party packages are also available.
The [`DRF-extensions` package][drf-extensions] includes a [`PaginateByMaxMixin` mixin class][paginate-by-max-mixin] that allows your API clients to specify `?page_size=max` to obtain the maximum allowed page size.
[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/
## drf-proxy-pagination
The [`drf-proxy-pagination` package][drf-proxy-pagination] includes a `ProxyPagination` class which allows to choose pagination class with a query parameter.
[cite]: https://docs.djangoproject.com/en/stable/topics/pagination/
[github-link-pagination]: https://developer.github.com/guides/traversing-with-pagination/
[link-header]: ../img/link-header-pagination.png
[drf-extensions]: http://chibisov.github.io/drf-extensions/docs/
[paginate-by-max-mixin]: http://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin
[disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/
[drf-proxy-pagination]: https://github.com/tuffnatty/drf-proxy-pagination
[disqus-cursor-api]: http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api
......@@ -35,7 +35,7 @@ The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSE
}
You can also set the parsers used for an individual view, or viewset,
using the `APIView` class based views.
using the `APIView` class-based views.
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
......@@ -52,6 +52,9 @@ using the `APIView` class based views.
Or, if you're using the `@api_view` decorator with function based views.
from rest_framework.decorators import api_view
from rest_framework.decorators import parser_classes
@api_view(['POST'])
@parser_classes((JSONParser,))
def example_view(request, format=None):
......@@ -90,7 +93,9 @@ You will typically want to use both `FormParser` and `MultiPartParser` together
Parses raw file upload content. The `request.data` property will be a dictionary with a single key `'file'` containing the uploaded file.
If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename. If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`.
If the view used with `FileUploadParser` is called with a `filename` URL keyword argument, then that argument will be used as the filename.
If it is called without a `filename` URL keyword argument, then the client must set the filename in the `Content-Disposition` HTTP header. For example `Content-Disposition: attachment; filename=upload.jpg`.
**.media_type**: `*/*`
......@@ -102,6 +107,7 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword
##### Basic usage example:
# views.py
class FileUploadView(views.APIView):
parser_classes = (FileUploadParser,)
......@@ -112,6 +118,11 @@ If the view used with `FileUploadParser` is called with a `filename` URL keyword
# ...
return Response(status=204)
# urls.py
urlpatterns = [
# ...
url(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())
]
---
......@@ -213,7 +224,7 @@ Modify your REST framework settings.
[jquery-ajax]: http://api.jquery.com/jQuery.ajax/
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
[upload-handlers]: https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#upload-handlers
[upload-handlers]: https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers
[rest-framework-yaml]: http://jpadilla.github.io/django-rest-framework-yaml/
[rest-framework-xml]: http://jpadilla.github.io/django-rest-framework-xml/
[yaml]: http://www.yaml.org/
......
......@@ -71,7 +71,7 @@ If not specified, this setting defaults to allowing unrestricted access:
)
You can also set the authentication policy on a per-view, or per-viewset basis,
using the `APIView` class based views.
using the `APIView` class-based views.
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
......@@ -92,7 +92,7 @@ Or, if you're using the `@api_view` decorator with function based views.
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
@api_view('GET')
@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
def example_view(request, format=None):
content = {
......@@ -100,6 +100,8 @@ Or, if you're using the `@api_view` decorator with function based views.
}
return Response(content)
__Note:__ when you set new permission classes through class attribute or decorators you're telling the view to ignore the default list set over the __settings.py__ file.
---
# API Reference
......@@ -130,7 +132,7 @@ This permission is suitable if you want to your API to allow read permissions to
## DjangoModelPermissions
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that has a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned.
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. This permission must only be applied to views that have a `.queryset` property set. Authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned.
* `POST` requests require the user to have the `add` permission on the model.
* `PUT` and `PATCH` requests require the user to have the `change` permission on the model.
......@@ -142,7 +144,7 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `
#### Using with views that do not include a `queryset` attribute.
If you're using this permission with a view that uses an overridden `get_queryset()` method there may not be a `queryset` attribute on the view. In this case we suggest also marking the view with a sential queryset, so that this class can determine the required permissions. For example:
If you're using this permission with a view that uses an overridden `get_queryset()` method there may not be a `queryset` attribute on the view. In this case we suggest also marking the view with a sentinel queryset, so that this class can determine the required permissions. For example:
queryset = User.objects.none() # Required for DjangoModelPermissions
......@@ -162,7 +164,7 @@ As with `DjangoModelPermissions`, this permission must only be applied to views
Note that `DjangoObjectPermissions` **does not** require the `django-guardian` package, and should support other object-level backends equally well.
As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoModelPermissions` and setting the `.perms_map` property. Refer to the source code for details.
As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoObjectPermissions` and setting the `.perms_map` property. Refer to the source code for details.
---
......@@ -257,14 +259,22 @@ The [REST Condition][rest-condition] package is another extension for building c
## DRY Rest Permissions
The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to define different permissions for individual default and custom actions. This package is made for apps with permissions that are derived from relationships defined in the app's data model. It also supports permission checks being returned to a client app through the API's serializer. Additionally it supports adding permissions to the default and custom list actions to restrict the data they retrive per user.
The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to define different permissions for individual default and custom actions. This package is made for apps with permissions that are derived from relationships defined in the app's data model. It also supports permission checks being returned to a client app through the API's serializer. Additionally it supports adding permissions to the default and custom list actions to restrict the data they retrieve per user.
## Django Rest Framework Roles
The [Django Rest Framework Roles][django-rest-framework-roles] package makes it easier to parameterize your API over multiple types of users.
## Django Rest Framework API Key
The [Django Rest Framework API Key][django-rest-framework-api-key] package allows you to ensure that every request made to the server requires an API key header. You can generate one from the django admin interface.
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
[authentication]: authentication.md
[throttling]: throttling.md
[filtering]: filtering.md
[contribauth]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#custom-permissions
[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
[contribauth]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions
[objectpermissions]: https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions
[guardian]: https://github.com/lukaszb/django-guardian
[get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user
[2.2-announcement]: ../topics/2.2-announcement.md
......@@ -273,3 +283,5 @@ The [DRY Rest Permissions][dry-rest-permissions] package provides the ability to
[composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions
[rest-condition]: https://github.com/caxap/rest_condition
[dry-rest-permissions]: https://github.com/Helioscene/dry-rest-permissions
[django-rest-framework-roles]: https://github.com/computer-lab/django-rest-framework-roles
[django-rest-framework-api-key]: https://github.com/manosim/django-rest-framework-api-key
......@@ -39,7 +39,7 @@ In order to explain the various types of relational fields, we'll use a couple o
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks')
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
......@@ -99,8 +99,8 @@ For example, the following serializer:
Would serialize to a representation like this:
{
'album_name': 'The Roots',
'artist': 'Undun',
'album_name': 'Undun',
'artist': 'The Roots',
'tracks': [
89,
90,
......@@ -286,9 +286,9 @@ Would serialize to a nested representation like this:
],
}
# Writable nested serializers
## Writable nested serializers
Be default nested serializers are read-only. If you want to to support write-operations to a nested serializer field you'll need to create either or both of the `create()` and/or `update()` methods, in order to explicitly specify how the child relationships should be saved.
By default nested serializers are read-only. If you want to support write-operations to a nested serializer field you'll need to create `create()` and/or `update()` methods in order to explicitly specify how the child relationships should be saved.
class TrackSerializer(serializers.ModelSerializer):
class Meta:
......@@ -324,12 +324,20 @@ Be default nested serializers are read-only. If you want to to support write-ope
>>> serializer.save()
<Album: Album object>
---
# Custom relational fields
In rare cases where none of the existing relational styles fit the representation you need,
you can implement a completely custom relational field, that describes exactly how the
output representation should be generated from the model instance.
To implement a custom relational field, you should override `RelatedField`, and implement the `.to_representation(self, value)` method. This method takes the target of the field as the `value` argument, and should return the representation that should be used to serialize the target. The `value` argument will typically be a model instance.
If you want to implement a read-write relational field, you must also implement the `.to_internal_value(self, data)` method.
To provide a dynamic queryset based on the `context`, you can also override `.get_queryset(self)` instead of specifying `.queryset` on the class or when initializing the field.
## Example
For example, we could define a relational field to serialize a track to a custom string representation, using its ordering, title, and duration.
......@@ -407,7 +415,7 @@ In this case we'd need to override `HyperlinkedRelatedField` to get the behavior
'organization_slug': obj.organization.slug,
'customer_pk': obj.pk
}
return reverse(view_name, url_kwargs, request=request, format=format)
return reverse(view_name, kwargs=url_kwargs, request=request, format=format)
def get_object(self, view_name, view_args, view_kwargs):
lookup_kwargs = {
......@@ -455,11 +463,13 @@ There are two keyword arguments you can use to control this behavior:
- `html_cutoff` - If set this will be the maximum number of choices that will be displayed by a HTML select drop down. Set to `None` to disable any limiting. Defaults to `1000`.
- `html_cutoff_text` - If set this will display a textual indicator if the maximum number of items have been cutoff in an HTML select drop down. Defaults to `"More than {count} items…"`
You can also control these globally using the settings `HTML_SELECT_CUTOFF` and `HTML_SELECT_CUTOFF_TEXT`.
In cases where the cutoff is being enforced you may want to instead use a plain input field in the HTML form. You can do so using the `style` keyword argument. For example:
assigned_to = serializers.SlugRelatedField(
queryset=User.objects.all(),
slug field='username',
slug_field='username',
style={'base_template': 'input.html'}
)
......@@ -474,7 +484,7 @@ Note that reverse relationships are not automatically included by the `ModelSeri
You'll normally want to ensure that you've set an appropriate `related_name` argument on the relationship, that you can use as the field name. For example:
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks')
album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
...
If you have not set a related name for the reverse relationship, you'll need to use the automatically generated related name in the `fields` argument. For example:
......@@ -487,7 +497,7 @@ See the Django documentation on [reverse relationships][reverse-relationships] f
## Generic relationships
If you want to serialize a generic foreign key, you need to define a custom field, to determine explicitly how you want serialize the targets of the relationship.
If you want to serialize a generic foreign key, you need to define a custom field, to determine explicitly how you want to serialize the targets of the relationship.
For example, given the following model for a tag, which has a generic relationship with other arbitrary models:
......@@ -495,17 +505,17 @@ For example, given the following model for a tag, which has a generic relationsh
"""
Tags arbitrary model instances using a generic relation.
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/
"""
tag_name = models.SlugField()
content_type = models.ForeignKey(ContentType)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
tagged_object = GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag_name
And the following two models, which may be have associated tags:
And the following two models, which may have associated tags:
class Bookmark(models.Model):
"""
......@@ -578,9 +588,14 @@ The following third party packages are also available.
The [drf-nested-routers package][drf-nested-routers] provides routers and relationship fields for working with nested resources.
## Rest Framework Generic Relations
The [rest-framework-generic-relations][drf-nested-relations] library provides read/write serialization for generic foreign keys.
[cite]: http://lwn.net/Articles/193245/
[reverse-relationships]: https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward
[reverse-relationships]: https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward
[routers]: http://www.django-rest-framework.org/api-guide/routers#defaultrouter
[generic-relations]: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#id1
[generic-relations]: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1
[2.2-announcement]: ../topics/2.2-announcement.md
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[drf-nested-relations]: https://github.com/Ian-Foote/rest-framework-generic-relations
......@@ -28,7 +28,7 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CL
}
You can also set the renderers used for an individual view, or viewset,
using the `APIView` class based views.
using the `APIView` class-based views.
from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer
......@@ -123,6 +123,8 @@ You can use `TemplateHTMLRenderer` either to return regular HTML pages using RES
If you're building websites that use `TemplateHTMLRenderer` along with other renderer classes, you should consider listing `TemplateHTMLRenderer` as the first class in the `renderer_classes` list, so that it will be prioritised first even for browsers that send poorly formed `ACCEPT:` headers.
See the [_HTML & Forms_ Topic Page][html-and-forms] for further examples of `TemplateHTMLRenderer` usage.
**.media_type**: `text/html`
**.format**: `'.html'`
......@@ -187,6 +189,15 @@ This renderer is suitable for CRUD-style web APIs that should also present a use
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'`
......@@ -197,9 +208,19 @@ Note that views that have nested or list serializers for their input won't work
## HTMLFormRenderer
Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `<form>` tags or an submit actions, as you'll probably need those to include the desired method and URL. Also note that the `HTMLFormRenderer` does not yet support including field error messages.
Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `<form>` tags, a hidden CSRF input or any submit buttons.
**Note**: The `HTMLFormRenderer` class is intended for internal use with the browsable API and admin interface. It should not be considered a fully documented or stable API. The template used by the `HTMLFormRenderer` class, and the context submitted to it **may be subject to change**. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely.
This renderer is not intended to be used directly, but can instead be used in templates by passing a serializer instance to the `render_form` template tag.
{% load rest_framework %}
<form action="/submit-report/" method="post">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save" />
</form>
For more information see the [HTML & Forms][html-and-forms] documentation.
**.media_type**: `text/html`
......@@ -207,7 +228,7 @@ Renders data returned by a serializer into an HTML form. The output of this ren
**.charset**: `utf-8`
**.template**: `'rest_framework/form.html'`
**.template**: `'rest_framework/horizontal/form.html'`
## MultiPartRenderer
......@@ -452,16 +473,21 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[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/dev/ref/template-response/#the-rendering-process
[cite]: https://docs.djangoproject.com/en/stable/stable/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]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[application/vnd.github+json]: http://developer.github.com/v3/media/
[application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/
[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views
[django-error-views]: https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views
[rest-framework-jsonp]: http://jpadilla.github.io/django-rest-framework-jsonp/
[cors]: http://www.w3.org/TR/cors/
[cors-docs]: http://www.django-rest-framework.org/topics/ajax-csrf-cors/
......@@ -486,3 +512,5 @@ Comma-separated values are a plain-text tabular data format, that can be easily
[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
......@@ -118,10 +118,6 @@ For more information see the [browser enhancements documentation].
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.
If you do need to access the raw content directly, you should use the `.stream` property in preference to using `request.content`, as it provides transparent support for browser-based non-form content.
For more information see the [browser enhancements documentation].
---
# Standard HttpRequest attributes
......
......@@ -91,5 +91,5 @@ As with any other `TemplateResponse`, this method is called to render the serial
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/dev/ref/template-response/
[cite]: https://docs.djangoproject.com/en/stable/stable/template-response/
[statuscodes]: status-codes.md
......@@ -23,7 +23,7 @@ There's no requirement for you to use them, but if you do then the self-describi
**Signature:** `reverse(viewname, *args, **kwargs)`
Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except that it returns a fully qualified URL, using the request to determine the host and port.
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:
......@@ -44,12 +44,12 @@ You should **include the request as a keyword argument** to the function, for ex
**Signature:** `reverse_lazy(viewname, *args, **kwargs)`
Has the same behavior as [`django.core.urlresolvers.reverse_lazy`][reverse-lazy], except that it returns a fully qualified URL, using the request to determine the host and port.
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]: http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5
[reverse]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse
[reverse-lazy]: https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-lazy
[reverse]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse
[reverse-lazy]: https://docs.djangoproject.com/en/stable/topics/http/urls/#reverse-lazy
......@@ -118,6 +118,26 @@ The above example would now generate the following URL pattern:
* URL pattern: `^users/{pk}/change-password/$` Name: `'user-change-password'`
In the case you do not want to use the default name generated for your custom action, you can use the url_name parameter to customize it.
For example, if you want to change the name of our custom action to `'user-change-password'`, you could write:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route
class UserViewSet(ModelViewSet):
...
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password')
def set_password(self, request, pk=None):
...
The above example would now generate the following URL pattern:
* URL pattern: `^users/{pk}/set_password/$` Name: `'user-change-password'`
You can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views.
For more information see the viewset documentation on [marking extra actions for routing][route-decorators].
# API Guide
......@@ -226,9 +246,9 @@ The following example will only route to the `list` and `retrieve` actions, and
),
Route(
url=r'^{prefix}/{lookup}$',
mapping={'get': 'retrieve'},
name='{basename}-detail',
initkwargs={'suffix': 'Detail'}
mapping={'get': 'retrieve'},
name='{basename}-detail',
initkwargs={'suffix': 'Detail'}
),
DynamicDetailRoute(
url=r'^{prefix}/{lookup}/{methodnamehyphen}$',
......@@ -250,7 +270,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a
lookup_field = 'username'
@detail_route()
def group_names(self, request):
def group_names(self, request, pk=None):
"""
Returns a list of all the group names that the given
user belongs to.
......
......@@ -36,7 +36,7 @@ The `api_settings` object will check for any user-defined settings, and otherwis
## API policy settings
*The following settings control the basic API policies, and are applied to every `APIView` class based view, or `@api_view` function based view.*
*The following settings control the basic API policies, and are applied to every `APIView` class-based view, or `@api_view` function based view.*
#### DEFAULT_RENDERER_CLASSES
......@@ -98,13 +98,19 @@ Default: `'rest_framework.negotiation.DefaultContentNegotiation'`
## Generic view settings
*The following settings control the behavior of the generic class based views.*
*The following settings control the behavior of the generic class-based views.*
#### DEFAULT_PAGINATION_SERIALIZER_CLASS
A class the determines the default serialization style for paginated responses.
---
**This setting has been removed.**
Default: `rest_framework.pagination.PaginationSerializer`
The pagination API does not use serializers to determine the output format, and
you'll need to instead override the `get_paginated_response method on a
pagination class in order to specify how the output format is controlled.
---
#### DEFAULT_FILTER_BACKENDS
......@@ -113,60 +119,39 @@ If set to `None` then generic filtering is disabled.
#### PAGINATE_BY
The default page size to use for pagination. If set to `None`, pagination is disabled by default.
Default: `None`
#### PAGINATE_BY_PARAM
---
**This setting is pending deprecation.**
**This setting has been removed.**
See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style).
---
The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If set to `None`, clients may not override the default page size.
For example, given the following settings:
REST_FRAMEWORK = {
'PAGINATE_BY': 10,
'PAGINATE_BY_PARAM': 'page_size',
}
#### PAGE_SIZE
A client would be able to modify the pagination size by using the `page_size` query parameter. For example:
GET http://example.com/api/accounts?page_size=25
The default page size to use for pagination. If set to `None`, pagination is disabled by default.
Default: `None`
#### MAX_PAGINATE_BY
#### PAGINATE_BY_PARAM
---
**This setting is pending deprecation.**
**This setting has been removed.**
See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style).
---
The maximum page size to allow when the page size is specified by the client. If set to `None`, then no maximum limit is applied.
For example, given the following settings:
#### MAX_PAGINATE_BY
REST_FRAMEWORK = {
'PAGINATE_BY': 10,
'PAGINATE_BY_PARAM': 'page_size',
'MAX_PAGINATE_BY': 100
}
---
A client request like the following would return a paginated list of up to 100 items.
**This setting is pending deprecation.**
GET http://example.com/api/accounts?page_size=999
See the pagination documentation for further guidance on [setting the pagination style](pagination.md#modifying-the-pagination-style).
Default: `None`
---
### SEARCH_PARAM
......@@ -196,7 +181,7 @@ If set, this value will restrict the set of versions that may be returned by the
Default: `None`
#### VERSION_PARAMETER
#### VERSION_PARAM
The string that should used for any versioning parameters, such as in the media type or URL query parameters.
......@@ -249,47 +234,45 @@ Default:
---
## Browser overrides
*The following settings provide URL or form-based overrides of the default browser behavior.*
#### FORM_METHOD_OVERRIDE
The name of a form field that may be used to override the HTTP method of the form.
If the value of this setting is `None` then form method overloading will be disabled.
## Schema generation controls
Default: `'_method'`
#### SCHEMA_COERCE_PATH_PK
#### FORM_CONTENT_OVERRIDE
If set, this maps the `'pk'` identifier in the URL conf onto the actual field
name when generating a schema path parameter. Typically this will be `'id'`.
This gives a more suitable representation as "primary key" is an implementation
detail, whereas "identifier" is a more general concept.
The name of a form field that may be used to override the content of the form payload. Must be used together with `FORM_CONTENTTYPE_OVERRIDE`.
Default: `True`
If either setting is `None` then form content overloading will be disabled.
#### SCHEMA_COERCE_METHOD_NAMES
Default: `'_content'`
If set, this is used to map internal viewset method names onto external action
names used in the schema generation. This allows us to generate names that
are more suitable for an external representation than those that are used
internally in the codebase.
#### FORM_CONTENTTYPE_OVERRIDE
Default: `{'retrieve': 'read', 'destroy': 'delete'}`
The name of a form field that may be used to override the content type of the form payload. Must be used together with `FORM_CONTENT_OVERRIDE`.
---
If either setting is `None` then form content overloading will be disabled.
## Content type controls
Default: `'_content_type'`
#### URL_FORMAT_OVERRIDE
#### URL_ACCEPT_OVERRIDE
The name of a URL parameter that may be used to override the default content negotiation `Accept` header behavior, by using a `format=…` query parameter in the request URL.
The name of a URL parameter that may be used to override the HTTP `Accept` header.
For example: `http://example.com/organizations/?format=csv`
If the value of this setting is `None` then URL accept overloading will be disabled.
If the value of this setting is `None` then URL format overrides will be disabled.
Default: `'accept'`
Default: `'format'`
#### URL_FORMAT_OVERRIDE
#### FORMAT_SUFFIX_KWARG
The name of a URL parameter that may be used to override the default `Accept` header based content negotiation.
The name of a parameter in the URL conf that may be used to provide a format suffix. This setting is applied when using `format_suffix_patterns` to include suffixed URL patterns.
If the value of this setting is `None` then URL format overloading will be disabled.
For example: `http://example.com/organizations.csv/`
Default: `'format'`
......@@ -421,6 +404,22 @@ This should be a function with the following signature:
Default: `'rest_framework.views.get_view_description'`
## HTML Select Field cutoffs
Global settings for [select field cutoffs for rendering relational fields](relations.md#select-field-cutoffs) in the browsable API.
#### HTML_SELECT_CUTOFF
Global setting for the `html_cutoff` value. Must be an integer.
Default: 1000
#### HTML_SELECT_CUTOFF_TEXT
A string representing a global setting for `html_cutoff_text`.
Default: `"More than {count} items..."`
---
## Miscellaneous settings
......@@ -451,19 +450,13 @@ A string representing the key that should be used for the URL fields generated b
Default: `'url'`
#### FORMAT_SUFFIX_KWARG
The name of a parameter in the URL conf that may be used to provide a format suffix.
Default: `'format'`
#### NUM_PROXIES
An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to `None` then less strict IP matching will be used by the throttle classes.
Default: `None`
[cite]: http://www.python.org/dev/peps/pep-0020/
[cite]: https://www.python.org/dev/peps/pep-0020/
[rfc4627]: http://www.ietf.org/rfc/rfc4627.txt
[heroku-minified-json]: https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses
[strftime]: http://docs.python.org/2/library/time.html#time.strftime
[strftime]: https://docs.python.org/3/library/time.html#time.strftime
......@@ -50,6 +50,7 @@ This class of status code indicates that the client's request was successfully r
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
## Redirection - 3xx
......@@ -86,9 +87,13 @@ The 4xx class of status code is intended for cases in which the client seems to
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
## Server Error - 5xx
......@@ -100,6 +105,7 @@ Response status codes beginning with the digit "5" indicate cases in which the s
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
## Helper functions
......
......@@ -162,7 +162,7 @@ The `credentials` method is appropriate for testing APIs that require authentica
#### .force_authenticate(user=None, token=None)
Sometimes you may want to bypass authentication, and simple force all requests by the test client to be automatically treated as authenticated.
Sometimes you may want to bypass authentication entirely and force all requests by the test client to be automatically treated as authenticated.
This can be a useful shortcut if you're testing the API but don't want to have to construct valid authentication credentials in order to make test requests.
......@@ -184,6 +184,110 @@ As usual CSRF validation will only apply to any session authenticated views. Th
---
# RequestsClient
REST framework also includes a client for interacting with your application
using the popular Python library, `requests`. This may be useful if:
* You are expecting to interface with the API primarily from another Python service,
and want to test the service at the same level as the client will see.
* You want to write tests in such a way that they can also be run against a staging or
live environment. (See "Live tests" below.)
This exposes exactly the same interface as if you were using a requests session
directly.
client = RequestsClient()
response = client.get('http://testserver/users/')
assert response.status_code == 200
Note that the requests client requires you to pass fully qualified URLs.
## `RequestsClient` and working with the database
The `RequestsClient` class is useful if you want to write tests that solely interact with the service interface. This is a little stricter than using the standard Django test client, as it means that all interactions should be via the API.
If you're using `RequestsClient` you'll want to ensure that test setup, and results assertions are performed as regular API calls, rather than interacting with the database models directly. For example, rather than checking that `Customer.objects.count() == 3` you would list the customers endpoint, and ensure that it contains three records.
## Headers & Authentication
Custom headers and authentication credentials can be provided in the same way
as [when using a standard `requests.Session` instance](http://docs.python-requests.org/en/master/user/advanced/#session-objects).
from requests.auth import HTTPBasicAuth
client.auth = HTTPBasicAuth('user', 'pass')
client.headers.update({'x-test': 'true'})
## CSRF
If you're using `SessionAuthentication` then you'll need to include a CSRF token
for any `POST`, `PUT`, `PATCH` or `DELETE` requests.
You can do so by following the same flow that a JavaScript based client would use.
First make a `GET` request in order to obtain a CRSF token, then present that
token in the following request.
For example...
client = RequestsClient()
# Obtain a CSRF token.
response = client.get('/homepage/')
assert response.status_code == 200
csrftoken = response.cookies['csrftoken']
# Interact with the API.
response = client.post('/organisations/', json={
'name': 'MegaCorp',
'status': 'active'
}, headers={'X-CSRFToken': csrftoken})
assert response.status_code == 200
## Live tests
With careful usage both the `RequestsClient` and the `CoreAPIClient` provide
the ability to write test cases that can run either in development, or be run
directly against your staging server or production environment.
Using this style to create basic tests of a few core piece of functionality is
a powerful way to validate your live service. Doing so may require some careful
attention to setup and teardown to ensure that the tests run in a way that they
do not directly affect customer data.
---
# CoreAPIClient
The CoreAPIClient allows you to interact with your API using the Python
`coreapi` client library.
# Fetch the API schema
client = CoreAPIClient()
schema = client.get('http://testserver/schema/')
# Create a new organisation
params = {'name': 'MegaCorp', 'status': 'active'}
client.action(schema, ['organisations', 'create'], params)
# Ensure that the organisation exists in the listing
data = client.action(schema, ['organisations', 'list'])
assert(len(data) == 1)
assert(data == [{'name': 'MegaCorp', 'status': 'active'}])
## Headers & Authentication
Custom headers and authentication may be used with `CoreAPIClient` in a
similar way as with `RequestsClient`.
from requests.auth import HTTPBasicAuth
client = CoreAPIClient()
client.session.auth = HTTPBasicAuth('user', 'pass')
client.session.headers.update({'x-test': 'true'})
---
# Test cases
REST framework includes the following test case classes, that mirror the existing Django test case classes, but use `APIClient` instead of Django's default `Client`.
......@@ -197,7 +301,7 @@ REST framework includes the following test case classes, that mirror the existin
You can use any of REST framework's test case classes as you would for the regular Django test case classes. The `self.client` attribute will be an `APIClient` instance.
from django.core.urlresolvers import reverse
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from myproject.apps.core.models import Account
......@@ -271,6 +375,6 @@ For example, to add support for using `format='html'` in test requests, you migh
}
[cite]: http://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper
[client]: https://docs.djangoproject.com/en/dev/topics/testing/tools/#the-test-client
[requestfactory]: https://docs.djangoproject.com/en/dev/topics/testing/advanced/#django.test.client.RequestFactory
[client]: https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client
[requestfactory]: https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory
[configuration]: #configuration
......@@ -41,7 +41,7 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C
The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `minute`, `hour` or `day` as the throttle period.
You can also set the throttling policy on a per-view or per-viewset basis,
using the `APIView` class based views.
using the `APIView` class-based views.
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
......@@ -184,12 +184,14 @@ If the `.wait()` method is implemented and the request is throttled, then a `Ret
The following is an example of a rate throttle, that will randomly throttle 1 in every 10 requests.
class RandomRateThrottle(throttles.BaseThrottle):
import random
class RandomRateThrottle(throttling.BaseThrottle):
def allow_request(self, request, view):
return random.randint(1, 10) == 1
return random.randint(1, 10) != 1
[cite]: https://dev.twitter.com/docs/error-codes-responses
[permissions]: permissions.md
[identifing-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster
[cache-setting]: https://docs.djangoproject.com/en/dev/ref/settings/#caches
[cache-docs]: https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache
[cache-setting]: https://docs.djangoproject.com/en/stable/ref/settings/#caches
[cache-docs]: https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache
......@@ -20,7 +20,7 @@ With `ModelForm` the validation is performed partially on the form, and partiall
* It is easy to switch between using shortcut `ModelSerializer` classes and using explicit `Serializer` classes. Any validation behavior being used for `ModelSerializer` is simple to replicate.
* Printing the `repr` of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behavior being called on the model instance.
When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using a `Serializer` classes instead, then you need to define the validation rules explicitly.
When you're using `ModelSerializer` all of this is handled automatically for you. If you want to drop down to using `Serializer` classes instead, then you need to define the validation rules explicitly.
#### Example
......@@ -61,9 +61,12 @@ It takes a single required argument, and an optional `messages` argument:
* `queryset` *required* - This is the queryset against which uniqueness should be enforced.
* `message` - The error message that should be used when validation fails.
* `lookup` - The lookup used to find an existing instance with the value being validated. Defaults to `'exact'`.
This validator should be applied to *serializer fields*, like so:
from rest_framework.validators import UniqueValidator
slug = SlugField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
......@@ -80,6 +83,8 @@ It has two required arguments, and a single optional `messages` argument:
The validator should be applied to *serializer classes*, like so:
from rest_framework.validators import UniqueTogetherValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
......@@ -114,6 +119,8 @@ These validators can be used to enforce the `unique_for_date`, `unique_for_month
The validator should be applied to *serializer classes*, like so:
from rest_framework.validators import UniqueForYearValidator
class ExampleSerializer(serializers.Serializer):
# ...
class Meta:
......@@ -146,17 +153,17 @@ The field will not be writable to the user, but the default value will still be
#### Using with a hidden date field.
If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns it's default value to the `validated_data` in the serializer.
If you want the date field to be entirely hidden from the user, then use `HiddenField`. This field type does not accept user input, but instead always returns its default value to the `validated_data` in the serializer.
published = serializers.HiddenField(default=timezone.now)
---
**Note**: The `UniqueFor<Range>Validation` classes always imposes an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input.
**Note**: The `UniqueFor<Range>Validation` classes impose an implicit constraint that the fields they are applied to are always treated as required. Fields with `default` values are an exception to this as they always supply a value even when omitted from user input.
---
# Advanced 'default' argument usage
# Advanced field defaults
Validators that are applied across multiple fields in the serializer can sometimes require a field input that should not be provided by the API client, but that *is* available as input to the validator.
......@@ -183,11 +190,76 @@ It takes a single argument, which is the default value or callable that should b
created_at = serializers.DateTimeField(
read_only=True,
default=CreateOnlyDefault(timezone.now)
default=serializers.CreateOnlyDefault(timezone.now)
)
---
# Limitations of validators
There are some ambiguous cases where you'll need to instead handle validation
explicitly, rather than relying on the default serializer classes that
`ModelSerializer` generates.
In these cases you may want to disable the automatically generated validators,
by specifying an empty list for the serializer `Meta.validators` attribute.
## Optional fields
By default "unique together" validation enforces that all fields be
`required=True`. In some cases, you might want to explicit apply
`required=False` to one of the fields, in which case the desired behaviour
of the validation is ambiguous.
In this case you will typically need to exclude the validator from the
serializer class, and instead write any validation logic explicitly, either
in the `.validate()` method, or else in the view.
For example:
class BillingRecordSerializer(serializers.ModelSerializer):
def validate(self, data):
# Apply custom validation either here, or in the view.
class Meta:
fields = ('client', 'date', 'amount')
extra_kwargs = {'client': {'required': 'False'}}
validators = [] # Remove a default "unique together" constraint.
## Updating nested serializers
When applying an update to an existing instance, uniqueness validators will
exclude the current instance from the uniqueness check. The current instance
is available in the context of the uniqueness check, because it exists as
an attribute on the serializer, having initially been passed using
`instance=...` when instantiating the serializer.
In the case of update operations on *nested* serializers there's no way of
applying this exclusion, because the instance is not available.
Again, you'll probably want to explicitly remove the validator from the
serializer class, and write the code the for the validation constraint
explicitly, in a `.validate()` method, or in the view.
## Debugging complex cases
If you're not sure exactly what behavior a `ModelSerializer` class will
generate it is usually a good idea to run `manage.py shell`, and print
an instance of the serializer, so that you can inspect the fields and
validators that it automatically generates for you.
>>> serializer = MyComplexModelSerializer()
>>> print(serializer)
class MyComplexModelSerializer:
my_fields = ...
Also keep in mind that with complex cases it can often be better to explicitly
define your serializer classes, rather than relying on the default
`ModelSerializer` behavior. This involves a little more code, but ensures
that the resulting behavior is more transparent.
---
# Writing custom validators
You can use any of Django's existing validators, or write your own custom validators.
......@@ -200,9 +272,15 @@ A validator may be any callable that raises a `serializers.ValidationError` on f
if value % 2 != 0:
raise serializers.ValidationError('This field must be an even number.')
## Class based
#### Field-level validation
You can specify custom field-level validation by adding `.validate_<field_name>` methods
to your `Serializer` subclass. This is documented in the
[Serializer docs](http://www.django-rest-framework.org/api-guide/serializers/#field-level-validation)
## Class-based
To write a class based validator, use the `__call__` method. Class based validators are useful as they allow you to parameterize and reuse behavior.
To write a class-based validator, use the `__call__` method. Class-based validators are useful as they allow you to parameterize and reuse behavior.
class MultipleOf(object):
def __init__(self, base):
......@@ -215,11 +293,11 @@ To write a class based validator, use the `__call__` method. Class based validat
#### Using `set_context()`
In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class based validator.
In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class-based validator.
def set_context(self, serializer_field):
# Determine if this is an update or a create operation.
# In `__call__` we can then use that information to modify the validation behavior.
self.is_update = serializer_field.parent.instance is not None
[cite]: https://docs.djangoproject.com/en/dev/ref/validators/
[cite]: https://docs.djangoproject.com/en/stable/ref/validators/
......@@ -71,8 +71,8 @@ You can also set the versioning scheme on an individual view. Typically you won'
The following settings keys are also used to control versioning:
* `DEFAULT_VERSION`. The value that should be used for `request.version` when no versioning information is present. Defaults to `None`.
* `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version if not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set. Defaults to `None`.
* `VERSION_PARAMETER`. The string that should used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`.
* `ALLOWED_VERSIONS`. If set, this value will restrict the set of versions that may be returned by the versioning scheme, and will raise an error if the provided version is not in this set. Note that the value used for the `DEFAULT_VERSION` setting is always considered to be part of the `ALLOWED_VERSIONS` set (unless it is `None`). Defaults to `None`.
* `VERSION_PARAM`. The string that should be used for any versioning parameters, such as in the media type or URL query parameters. Defaults to `'version'`.
You can also set your versioning class plus those three values on a per-view or a per-viewset basis by defining your own versioning scheme and using the `default_version`, `allowed_versions` and `version_param` class variables. For example, if you want to use `URLPathVersioning`:
......@@ -130,12 +130,12 @@ Your URL conf must include a pattern that matches the version with a `'version'`
urlpatterns = [
url(
r'^(?P<version>[v1|v2]+)/bookings/$',
r'^(?P<version>(v1|v2))/bookings/$',
bookings_list,
name='bookings-list'
),
url(
r'^(?P<version>[v1|v2]+)/bookings/(?P<pk>[0-9]+)/$',
r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',
bookings_detail,
name='bookings-detail'
)
......@@ -143,7 +143,7 @@ Your URL conf must include a pattern that matches the version with a `'version'`
## NamespaceVersioning
To the client, this scheme is the same as `URLParameterVersioning`. The only difference is how it is configured in your Django application, as it uses URL namespacing, instead of URL keyword arguments.
To the client, this scheme is the same as `URLPathVersioning`. The only difference is how it is configured in your Django application, as it uses URL namespacing, instead of URL keyword arguments.
GET /v1/something/ HTTP/1.1
Host: example.com
......@@ -165,7 +165,7 @@ In the following example we're giving a set of views two different possible URL
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
Both `URLParameterVersioning` and `NamespaceVersioning` are reasonable if you just need a simple versioning scheme. The `URLParameterVersioning` approach might be better suitable for small ad-hoc projects, and the `NamespaceVersioning` is probably easier to manage for larger projects.
Both `URLPathVersioning` and `NamespaceVersioning` are reasonable if you just need a simple versioning scheme. The `URLPathVersioning` approach might be better suitable for small ad-hoc projects, and the `NamespaceVersioning` is probably easier to manage for larger projects.
## HostNameVersioning
......@@ -214,7 +214,7 @@ If your versioning scheme is based on the request URL, you will also want to alt
[cite]: http://www.slideshare.net/evolve_conference/201308-fielding-evolve/31
[roy-fielding-on-versioning]: http://www.infoq.com/articles/roy-fielding-on-versioning
[klabnik-guidelines]: http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned
[heroku-guidelines]: https://github.com/interagent/http-api-design#version-with-accepts-header
[heroku-guidelines]: https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md
[json-parameters]: http://tools.ietf.org/html/rfc4627#section-6
[vendor-media-type]: http://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree
[lvh]: https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains
source: decorators.py
views.py
# Class Based Views
# Class-based Views
> Django's class based views are a welcome departure from the old-style views.
> Django's class-based views are a welcome departure from the old-style views.
>
> &mdash; [Reinout van Rees][cite]
......@@ -73,6 +73,8 @@ The following methods are used by REST framework to instantiate the various plug
### .get_content_negotiator(self)
### .get_exception_handler(self)
## API policy implementation methods
The following methods are called before dispatching to the handler method.
......@@ -119,7 +121,7 @@ You won't typically need to override this method.
# Function Based Views
> Saying [that Class based views] is always the superior solution is a mistake.
> Saying [that class-based views] is always the superior solution is a mistake.
>
> &mdash; [Nick Coghlan][cite2]
......@@ -127,7 +129,7 @@ REST framework also allows you to work with regular function based views. It pr
## @api_view()
**Signature:** `@api_view(http_method_names=['GET'])`
**Signature:** `@api_view(http_method_names=['GET'], exclude_from_schema=False)`
The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data:
......@@ -139,7 +141,7 @@ The core of this functionality is the `api_view` decorator, which takes a list o
This view will use the default renderers, parsers, authentication classes etc specified in the [settings].
By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behavior, specify which methods the view allows, like so:
By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behaviour, specify which methods the view allows, like so:
@api_view(['GET', 'POST'])
def hello_world(request):
......@@ -147,6 +149,13 @@ By default only `GET` methods will be accepted. Other methods will respond with
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})
You can also mark an API view as being omitted from any [auto-generated schema][schemas],
using the `exclude_from_schema` argument.:
@api_view(['GET'], exclude_from_schema=True)
def api_docs(request):
...
## API policy decorators
To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes:
......@@ -178,3 +187,4 @@ Each of these decorators takes a single argument which must be a list or tuple o
[cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html
[settings]: settings.md
[throttling]: throttling.md
[schemas]: schemas.md
......@@ -179,7 +179,7 @@ In order to use a `GenericViewSet` class you'll override the class and either mi
The `ModelViewSet` class inherits from `GenericAPIView` and includes implementations for various actions, by mixing in the behavior of the various mixin classes.
The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`, `.create()`, `.update()`, and `.destroy()`.
The actions provided by the `ModelViewSet` class are `.list()`, `.retrieve()`, `.create()`, `.update()`, `.partial_update()`, and `.destroy()`.
#### Example
......@@ -235,6 +235,8 @@ You may need to provide custom `ViewSet` classes that do not have the full set o
To create a base viewset class that provides `create`, `list` and `retrieve` operations, inherit from `GenericViewSet`, and mixin the required actions:
from rest_framework import mixins
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
......
......@@ -147,13 +147,13 @@ When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentity
From version 2.2 onwards, serializers with hyperlinked relationships *always* require a `'request'` key to be supplied in the context dictionary. The implicit behavior will continue to function, but its use will raise a `PendingDeprecationWarning`.
[xordoquy]: https://github.com/xordoquy
[django-python-3]: https://docs.djangoproject.com/en/dev/faq/install/#can-i-use-django-with-python-3
[porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/
[python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility
[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy
[django-python-3]: https://docs.djangoproject.com/en/stable/faq/install/#can-i-use-django-with-python-3
[porting-python-3]: https://docs.djangoproject.com/en/stable/topics/python3/
[python-compat]: https://docs.djangoproject.com/en/stable/releases/1.5/#python-compatibility
[django-deprecation-policy]: https://docs.djangoproject.com/en/stable/internals/release-process/#internal-release-deprecation-policy
[credits]: http://www.django-rest-framework.org/topics/credits
[mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[django-rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs
[marcgibbons]: https://github.com/marcgibbons/
[issues]: https://github.com/tomchristie/django-rest-framework/issues
[564]: https://github.com/tomchristie/django-rest-framework/issues/564
[issues]: https://github.com/encode/django-rest-framework/issues
[564]: https://github.com/encode/django-rest-framework/issues/564
......@@ -6,7 +6,7 @@ REST framework 2.3 makes it even quicker and easier to build your Web APIs.
The 2.3 release introduces the [ViewSet][viewset] and [Router][router] classes.
A viewset is simply a type of class based view that allows you to group multiple views into a single common class.
A viewset is simply a type of class-based view that allows you to group multiple views into a single common class.
Routers allow you to automatically determine the URLconf for your viewset classes.
......
......@@ -39,7 +39,7 @@ Then run the `runtests.py` script.
./runtests.py
The new test runner also includes [flake8](https://flake8.readthedocs.org) code linting, which should help keep our coding style consistent.
The new test runner also includes [flake8](https://flake8.readthedocs.io) code linting, which should help keep our coding style consistent.
#### Test runner flags
......@@ -162,11 +162,11 @@ The next planned release will be 3.0, featuring an improved and simplified seria
Once again, many thanks to all the generous [backers and sponsors][kickstarter-sponsors] who've helped make this possible!
[lts-releases]: https://docs.djangoproject.com/en/dev/internals/release-process/#long-term-support-lts-releases
[lts-releases]: https://docs.djangoproject.com/en/stable/internals/release-process/#long-term-support-lts-releases
[2-4-release-notes]: release-notes#240
[view-name-and-description-settings]: ../api-guide/settings#view-names-and-descriptions
[client-ip-identification]: ../api-guide/throttling#how-clients-are-identified
[2-3-announcement]: 2.3-announcement
[github-labels]: https://github.com/tomchristie/django-rest-framework/issues
[github-milestones]: https://github.com/tomchristie/django-rest-framework/milestones
[github-labels]: https://github.com/encode/django-rest-framework/issues
[github-milestones]: https://github.com/encode/django-rest-framework/milestones
[kickstarter-sponsors]: kickstarter-announcement#sponsors
......@@ -426,7 +426,7 @@ There are four methods that can be overridden, depending on what functionality y
* `.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`.
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.
......@@ -801,7 +801,7 @@ This change means that you can now easily customize the style of error responses
## The metadata API
Behavior for dealing with `OPTIONS` requests was previously built directly into the class based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework.
Behavior for dealing with `OPTIONS` requests was previously built directly into the class-based views. This has now been properly separated out into a Metadata API that allows the same pluggable style as other API policies in REST framework.
This makes it far easier to use a different style for `OPTIONS` responses throughout your API, and makes it possible to create third-party metadata policies.
......@@ -870,7 +870,7 @@ The `COMPACT_JSON` setting has been added, and can be used to revert this behavi
#### File fields as URLs
The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/dev/howto/static-files/#serving-uploaded-files-in-development).
The `FileField` and `ImageField` classes are now represented as URLs by default. You should ensure you set Django's [standard `MEDIA_URL` setting](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-MEDIA_URL) appropriately, and ensure your application [serves the uploaded files](https://docs.djangoproject.com/en/stable/howto/static-files/#serving-uploaded-files-in-development).
You can revert this behavior, and display filenames in the representation by using the `UPLOADED_FILES_USE_URL` settings key:
......@@ -894,11 +894,11 @@ If the request is omitted from the context, the returned URLs will be of the for
The custom `X-Throttle-Wait-Second` header has now been dropped in favor of the standard `Retry-After` header. You can revert this behavior if needed by writing a custom exception handler for your application.
#### Date and time objects as ISO-8859-1 strings in serializer data.
#### Date and time objects as ISO-8601 strings in serializer data.
Date and Time objects are now coerced to strings by default in the serializer output. Previously they were returned as `Date`, `Time` and `DateTime` objects, and later coerced to strings by the renderer.
You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8859-1'` will result in native objects being returned in serializer data.
You can modify this behavior globally by settings the existing `DATE_FORMAT`, `DATETIME_FORMAT` and `TIME_FORMAT` settings keys. Setting these values to `None` instead of their default value of `'iso-8601'` will result in native objects being returned in serializer data.
REST_FRAMEWORK = {
# Return native `Date` and `Time` objects in `serializer.data`
......@@ -957,9 +957,9 @@ The 3.1 release is planned to address improvements in the following components:
The 3.2 release is planned to introduce an alternative admin-style interface to the browsable API.
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/tomchristie/django-rest-framework/milestones).
You can follow development on the GitHub site, where we use [milestones to indicate planning timescales](https://github.com/encode/django-rest-framework/milestones).
[kickstarter]: http://kickstarter.com/projects/tomchristie/django-rest-framework-3
[sponsors]: http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors
[mixins.py]: https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py
[django-localization]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/#localization-how-to-create-language-files
[mixins.py]: https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py
[django-localization]: https://docs.djangoproject.com/en/stable/topics/i18n/translation/#localization-how-to-create-language-files
......@@ -30,7 +30,7 @@ Note that as a result of this work a number of settings keys and generic view at
Until now, there has only been a single built-in pagination style in REST framework. We now have page, limit/offset and cursor based schemes included by default.
The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api/) on the subject.
The cursor based pagination scheme is particularly smart, and is a better approach for clients iterating through large or frequently changing result sets. The scheme supports paging against non-unique indexes, by using both cursor and limit/offset information. It also allows for both forward and reverse cursor pagination. Much credit goes to David Cramer for [this blog post](http://cramer.io/2011/03/08/building-cursors-for-the-disqus-api) on the subject.
#### Pagination controls in the browsable API.
......@@ -110,7 +110,7 @@ When per-request internationalization is enabled, client requests will respect t
"detail": "No se ha podido satisfacer la solicitud de cabecera de Accept."
}
Note that the structure of the error responses is still the same. We still have a `details` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler].
Note that the structure of the error responses is still the same. We still have a `detail` key in the response. If needed you can modify this behavior too, by using a [custom exception handler][custom-exception-handler].
We include built-in translations both for standard exception cases, and for serializer validation errors.
......@@ -153,7 +153,7 @@ For more information, see the documentation on [customizing field mappings][cust
We've now moved a number of packages out of the core of REST framework, and into separately installable packages. If you're currently using these you don't need to worry, you simply need to `pip install` the new packages, and change any import paths.
We're making this change in order to help distribute the maintainance workload, and keep better focus of the core essentials of the framework.
We're making this change in order to help distribute the maintenance workload, and keep better focus of the core essentials of the framework.
The change also means we can be more flexible with which external packages we recommend. For example, the excellently maintained [Django OAuth toolkit](https://github.com/evonove/django-oauth-toolkit) has now been promoted as our recommended option for integrating OAuth support.
......
......@@ -8,7 +8,7 @@ This interface is intended to act as a more user-friendly interface to the API.
We've also fixed a huge number of issues, and made numerous cleanups and improvements.
Over the course of the 3.1.x series we've [resolved nearly 600 tickets](https://github.com/tomchristie/django-rest-framework/issues?utf8=%E2%9C%93&q=closed%3A%3E2015-03-05) on our GitHub issue tracker. This means we're currently running at a rate of **closing around 100 issues or pull requests per month**.
Over the course of the 3.1.x series we've [resolved nearly 600 tickets](https://github.com/encode/django-rest-framework/issues?utf8=%E2%9C%93&q=closed%3A%3E2015-03-05) on our GitHub issue tracker. This means we're currently running at a rate of **closing around 100 issues or pull requests per month**.
None of this would have been possible without the support of our wonderful Kickstarter backers. If you're looking for a job in Django development we'd strongly recommend taking [a look through our sponsors](http://www.django-rest-framework.org/topics/kickstarter-announcement/#sponsors) and finding out who's hiring.
......@@ -54,7 +54,7 @@ The following pagination view attributes and settings have been moved into attri
* `view.max_paginate_by` - Use `paginator.max_page_size` instead.
* `settings.PAGINATE_BY` - Use `paginator.page_size` instead.
* `settings.PAGINATE_BY_PARAM` - Use `paginator.page_size_query_param` instead.
* `settings.MAX_PAGINATE_BY` - Use `max_page_size` instead.
* `settings.MAX_PAGINATE_BY` - Use `paginator.max_page_size` instead.
## Modifications to list behaviors
......
# Django REST framework 3.3
The 3.3 release marks the final work in the Kickstarter funded series. We'd like to offer a final resounding **thank you** to all our wonderful sponsors and supporters.
The amount of work that has been achieved as a direct result of the funding is immense. We've added a huge amounts of new functionality, resolved nearly 2,000 tickets, and redesigned & refined large parts of the project.
In order to continue driving REST framework forward, we'll shortly be announcing a new set of funding plans. Follow [@_tomchristie](https://twitter.com/_tomchristie) to keep up to date with these announcements, and be among the first set of sign ups.
We strongly believe that collaboratively funded software development yields outstanding results for a relatively low investment-per-head. If you or your company use REST framework commercially, then we would strongly urge you to participate in this latest funding drive, and help us continue to build an increasingly polished & professional product.
---
## Release notes
Significant new functionality in the 3.3 release includes:
* Filters presented as HTML controls in the browsable API.
* A [forms API][forms-api], allowing serializers to be rendered as HTML forms.
* Django 1.9 support.
* A [`JSONField` serializer field][jsonfield], corresponding to Django 1.9's Postgres `JSONField` model field.
* Browsable API support [via AJAX][ajax-form], rather than server side request overloading.
![Filter Controls](../img/filter-controls.png)
*Example of the new filter controls*
---
## Supported versions
This release drops support for Django 1.5 and 1.6. Django 1.7, 1.8 or 1.9 are now required.
This brings our supported versions into line with Django's [currently supported versions][django-supported-versions]
## Deprecations
The AJAX based support for the browsable API means that there are a number of internal cleanups in the `request` class. For the vast majority of developers this should largely remain transparent:
* To support form based `PUT` and `DELETE`, or to support form content types such as JSON, you should now use the [AJAX forms][ajax-form] javascript library. This replaces the previous 'method and content type overloading' that required significant internal complexity to the request class.
* The `accept` query parameter is no longer supported by the default content negotiation class. If you require it then you'll need to [use a custom content negotiation class](browser-enhancements.md#url-based-accept-headers).
* The custom `HTTP_X_HTTP_METHOD_OVERRIDE` header is no longer supported by default. If you require it then you'll need to [use custom middleware](browser-enhancements.md#http-header-based-method-overriding).
The following pagination view attributes and settings have been moved into attributes on the pagination class since 3.1. Their usage was formerly deprecated, and has now been removed entirely, in line with the deprecation policy.
* `view.paginate_by` - Use `paginator.page_size` instead.
* `view.page_query_param` - Use `paginator.page_query_param` instead.
* `view.paginate_by_param` - Use `paginator.page_size_query_param` instead.
* `view.max_paginate_by` - Use `paginator.max_page_size` instead.
* `settings.PAGINATE_BY` - Use `paginator.page_size` instead.
* `settings.PAGINATE_BY_PARAM` - Use `paginator.page_size_query_param` instead.
* `settings.MAX_PAGINATE_BY` - Use `paginator.max_page_size` instead.
The `ModelSerializer` and `HyperlinkedModelSerializer` classes should now include either a `fields` or `exclude` option, although the `fields = '__all__'` shortcut may be used. Failing to include either of these two options is currently pending deprecation, and will be removed entirely in the 3.5 release. This behavior brings `ModelSerializer` more closely in line with Django's `ModelForm` behavior.
[forms-api]: html-and-forms.md
[ajax-form]: https://github.com/encode/ajax-form
[jsonfield]: ../../api-guide/fields#jsonfield
[django-supported-versions]: https://www.djangoproject.com/download/#supported-versions
\ No newline at end of file
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>
# Django REST framework 3.4
The 3.4 release is the first in a planned series that will be addressing schema
generation, hypermedia support, API clients, and finally realtime support.
---
## Funding
The 3.4 release has been made possible a recent [Mozilla grant][moss], and by our
[collaborative funding model][funding]. If you use REST framework commercially, and would
like to see this work continue, we strongly encourage you to invest in its
continued development by **[signing up for a paid plan][funding]**.
The initial aim is to provide a single full-time position on REST framework.
Right now we're over 60% of the way towards achieving that.
*Every single sign-up makes a significant impact.*
<ul class="premium-promo promo">
<li><a href="http://jobs.rover.com/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)">Rover.com</a></li>
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
*Many thanks to all our [awesome sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), and [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf).*
---
## Schemas & client libraries
REST framework 3.4 brings built-in support for generating API schemas.
We provide this support by using [Core API][core-api], a Document Object Model
for describing APIs.
Because Core API represents the API schema in an format-independent
manner, we're able to render the Core API `Document` object into many different
schema formats, by allowing the renderer class to determine how the internal
representation maps onto the external schema format.
This approach should also open the door to a range of auto-generated API
documentation options in the future, by rendering the `Document` object into
HTML documentation pages.
Alongside the built-in schema support, we're also now providing the following:
* A [command line tool][command-line-client] for interacting with APIs.
* A [Python client library][client-library] for interacting with APIs.
These API clients are dynamically driven, and able to interact with any API
that exposes a supported schema format.
Dynamically driven clients allow you to interact with an API at an application
layer interface, rather than a network layer interface, while still providing
the benefits of RESTful Web API design.
We're expecting to expand the range of languages that we provide client libraries
for over the coming months.
Further work on maturing the API schema support is also planned, including
documentation on supporting file upload and download, and improved support for
documentation generation and parameter annotation.
---
Current support for schema formats is as follows:
Name | Support | PyPI package
---------------------------------|-------------------------------------|--------------------------------
[Core JSON][core-json] | Schema generation & client support. | Built-in support in `coreapi`.
[Swagger / OpenAPI][swagger] | Schema generation & client support. | The `openapi-codec` package.
[JSON Hyper-Schema][hyperschema] | Currently client support only. | The `hyperschema-codec` package.
[API Blueprint][api-blueprint] | Not yet available. | Not yet available.
---
You can read more about any of this new functionality in the following:
* New tutorial section on [schemas & client libraries][tut-7].
* Documentation page on [schema generation][schema-generation].
* Topic page on [API clients][api-clients].
It is also worth noting that Marc Gibbons is currently working towards a 2.0 release of
the popular Django REST Swagger package, which will tie in with our new built-in support.
---
## Supported versions
The 3.4.0 release adds support for Django 1.10.
The following versions of Python and Django are now supported:
* Django versions 1.8, 1.9, and 1.10.
* Python versions 2.7, 3.2(\*), 3.3(\*), 3.4, 3.5.
(\*) Note that Python 3.2 and 3.3 are not supported from Django 1.9 onwards.
---
## Deprecations and changes
The 3.4 release includes very limited deprecation or behavioral changes, and
should present a straightforward upgrade.
### Use fields or exclude on serializer classes.
The following change in 3.3.0 is now escalated from "pending deprecation" to
"deprecated". Its usage will continue to function but will raise warnings:
`ModelSerializer` and `HyperlinkedModelSerializer` should include either a `fields`
option, or an `exclude` option. The `fields = '__all__'` shortcut may be used
to explicitly include all fields.
### Microsecond precision when returning time or datetime.
Using the default JSON renderer and directly returning a `datetime` or `time`
instance will now render with microsecond precision (6 digits), rather than
millisecond precision (3 digits). This makes the output format consistent with the
default string output of `serializers.DateTimeField` and `serializers.TimeField`.
This change *does not affect the default behavior when using serializers*,
which is to serialize `datetime` and `time` instances into strings with
microsecond precision.
The serializer behavior can be modified if needed, using the `DATETIME_FORMAT`
and `TIME_FORMAT` settings.
The renderer behavior can be modified by setting a custom `encoder_class`
attribute on a `JSONRenderer` subclass.
### Relational choices no longer displayed in OPTIONS requests.
Making an `OPTIONS` request to views that have a serializer choice field
will result in a list of the available choices being returned in the response.
In cases where there is a relational field, the previous behavior would be
to return a list of available instances to choose from for that relational field.
In order to minimise exposed information the behavior now is to *not* return
choices information for relational fields.
If you want to override this new behavior you'll need to [implement a custom
metadata class][metadata].
See [issue #3751][gh3751] for more information on this behavioral change.
---
## Other improvements
This release includes further work from a huge number of [pull requests and issues][milestone].
Many thanks to all our contributors who've been involved in the release, either through raising issues, giving feedback, improving the documentation, or suggesting and implementing code changes.
The full set of itemized release notes [are available here][release-notes].
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[moss]: mozilla-grant.md
[funding]: funding.md
[core-api]: http://www.coreapi.org/
[command-line-client]: api-clients#command-line-client
[client-library]: api-clients#python-client-library
[core-json]: http://www.coreapi.org/specification/encoding/#core-json-encoding
[swagger]: https://openapis.org/specification
[hyperschema]: http://json-schema.org/latest/json-schema-hypermedia.html
[api-blueprint]: https://apiblueprint.org/
[tut-7]: ../../tutorial/7-schemas-and-client-libraries/
[schema-generation]: ../../api-guide/schemas/
[api-clients]: api-clients.md
[milestone]: https://github.com/encode/django-rest-framework/milestone/35
[release-notes]: release-notes#34
[metadata]: ../../api-guide/metadata/#custom-metadata-classes
[gh3751]: https://github.com/encode/django-rest-framework/issues/3751
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>
# Django REST framework 3.6
The 3.6 release adds two major new features to REST framework.
1. Built-in interactive API documentation support.
2. A new JavaScript client&nbsp;library.
![API Documentation](/img/api-docs.gif)
*Above: The interactive API documentation.*
---
## Funding
The 3.6 release would not have been possible without our [backing from Mozilla](mozilla-grant.md) to the project, and our [collaborative funding&nbsp;model][funding].
If you use REST framework commercially and would like to see this work continue,
we strongly encourage you to invest in its continued development by
**[signing up for a paid&nbsp;plan][funding]**.
<ul class="premium-promo promo">
<li><a href="http://jobs.rover.com/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)">Rover.com</a></li>
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
<li><a href="https://hello.machinalis.co.uk/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)">Machinalis</a></li>
<li><a href="https://rollbar.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rollbar.png)">Rollbar</a></li>
<li><a href="https://micropyramid.com/django-rest-framework-development-services/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/mp-text-logo.png)">MicroPyramid</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), [Rollbar](https://rollbar.com), and [MicroPyramid](https://micropyramid.com/django-rest-framework-development-services/).*
---
## Interactive API documentation
REST framework's new API documentation supports a number of features:
* Live API interaction.
* Support for various authentication schemes.
* Code snippets for the Python, JavaScript, and Command Line clients.
The `coreapi` library is required as a dependancy for the API docs. Make sure
to install the latest version (2.3.0 or above). The `pygments` and `markdown`
libraries are optional but recommended.
To install the API documentation, you'll need to include it in your projects URLconf:
from rest_framework.documentation import include_docs_urls
API_TITLE = 'API title'
API_DESCRIPTION = '...'
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION))
]
Once installed you should see something a little like this:
![API Documentation](/img/api-docs.png)
We'll likely be making further refinements to the API documentation over the
coming weeks. Keep in mind that this is a new feature, and please do give
us feedback if you run into any issues or limitations.
For more information on documenting your API endpoints see the ["Documenting your API"][api-docs] section.
---
## JavaScript client library
The JavaScript client library allows you to load an API schema, and then interact
with that API at an application layer interface, rather than constructing fetch
requests explicitly.
Here's a brief example that demonstrates:
* Loading the client library and schema.
* Instantiating an authenticated client.
* Making an API request using the client.
**index.html**
<html>
<head>
<script src="/static/rest_framework/js/coreapi-0.1.0.js"></script>
<script src="/docs/schema.js"></script>
<script>
const coreapi = window.coreapi
const schema = window.schema
// Instantiate a client...
let auth = coreapi.auth.TokenAuthentication({scheme: 'JWT', token: 'xxx'})
let client = coreapi.Client({auth: auth})
// Make an API request...
client.action(schema, ['projects', 'list']).then(function(result) {
alert(result)
})
</script>
</head>
</html>
The JavaScript client library supports various authentication schemes, and can be
used by your project itself, or as an external client interacting with your API.
The client is not limited to usage with REST framework APIs, although it does
currently only support loading CoreJSON API schemas. Support for Swagger and
other API schemas is planned.
For more details see the [JavaScript client library documentation][js-docs].
## Authentication classes for the Python client library
Previous authentication support in the Python client library was limited to
allowing users to provide explicit header values.
We now have better support for handling the details of authentication, with
the introduction of the `BasicAuthentication`, `TokenAuthentication`, and
`SessionAuthentication` schemes.
You can include the authentication scheme when instantiating a new client.
auth = coreapi.auth.TokenAuthentication(scheme='JWT', token='xxx-xxx-xxx')
client = coreapi.Client(auth=auth)
For more information see the [Python client library documentation][py-docs].
---
## Deprecations
### Updating coreapi
If you're using REST framework's schema generation, or want to use the API docs,
then you'll need to update to the latest version of coreapi. (2.3.0)
### Generating schemas from Router
The 3.5 "pending deprecation" of router arguments for generating a schema view, such as `schema_title`, `schema_url` and `schema_renderers`, have now been escalated to a
"deprecated" warning.
Instead of using `DefaultRouter(schema_title='Example API')`, you should use the `get_schema_view()` function, and include the view explicitly in your URL conf.
### DjangoFilterBackend
The 3.5 "pending deprecation" warning of the built-in `DjangoFilterBackend` has now
been escalated to a "deprecated" warning.
You should change your imports and REST framework filter settings as follows:
* `rest_framework.filters.DjangoFilterBackend` becomes `django_filters.rest_framework.DjangoFilterBackend`.
* `rest_framework.filters.FilterSet` becomes `django_filters.rest_framework.FilterSet`.
---
## What's next
There are likely to be a number of refinements to the API documentation and
JavaScript client library over the coming weeks, which could include some of the following:
* Support for private API docs, requiring login.
* File upload and download support in the JavaScript client & API docs.
* Comprehensive documentation for the JavaScript client library.
* Automatically including authentication details in the API doc code snippets.
* Adding authentication support in the command line client.
* Support for loading Swagger and other schemas in the JavaScript client.
* Improved support for documenting parameter schemas and response schemas.
* Refining the API documentation interaction modal.
Once work on those refinements is complete, we'll be starting feature work
on realtime support, for the 3.7 release.
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[funding]: funding.md
[api-docs]: documenting-your-api.md
[js-docs]: api-clients.md#javascript-client-library
[py-docs]: api-clients.md#python-client-library
......@@ -35,7 +35,7 @@ The best way to deal with CORS in REST framework is to add the required response
[cite]: http://www.codinghorror.com/blog/2008/10/preventing-csrf-and-xsrf-attacks.html
[csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
[csrf-ajax]: https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
[csrf-ajax]: https://docs.djangoproject.com/en/stable/ref/csrf/#ajax
[cors]: http://www.w3.org/TR/cors/
[ottoyiu]: https://github.com/ottoyiu/
[django-cors-headers]: https://github.com/ottoyiu/django-cors-headers/
......@@ -17,7 +17,7 @@ By default, the API will return the format specified by the headers, which in th
## Customizing
The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 2.1.1), making it easy to customize the look-and-feel.
The browsable API is built with [Twitter's Bootstrap][bootstrap] (v 3.3.5), making it easy to customize the look-and-feel.
To customize the default style, create a template called `rest_framework/api.html` that extends from `rest_framework/base.html`. For example:
......@@ -146,8 +146,6 @@ An alternative, but more complex option would be to replace the input with an au
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.
Better support for autocomplete inputs is planned in future versions.
---
[cite]: http://en.wikiquote.org/wiki/Alfred_North_Whitehead
......@@ -163,4 +161,4 @@ Better support for autocomplete inputs is planned in future versions.
[bcomponentsnav]: http://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
[django-autocomplete-light-install]: http://django-autocomplete-light.readthedocs.org/en/latest/#install
[django-autocomplete-light-install]: https://django-autocomplete-light.readthedocs.io/en/master/install.html
......@@ -4,67 +4,74 @@
>
> &mdash; [RESTful Web Services][cite], Leonard Richardson & Sam Ruby.
## Browser based PUT, DELETE, etc...
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.
REST framework supports browser-based `PUT`, `DELETE` and other methods, by
overloading `POST` requests using a hidden form field.
## Browser based PUT, DELETE, etc...
Note that this is the same strategy as is used in [Ruby on Rails][rails].
The [AJAX form library][ajax-form] supports browser-based `PUT`, `DELETE` and other methods on HTML forms.
For example, given the following form:
After including the library, use the `data-method` attribute on the form, like so:
<form action="/news-items/5" method="POST">
<input type="hidden" name="_method" value="DELETE">
<form action="/" data-method="PUT">
<input name='foo'/>
...
</form>
`request.method` would return `"DELETE"`.
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.
## HTTP header based method overriding
## Browser based submission of non-form content
REST framework also supports method overriding via the semi-standard `X-HTTP-Method-Override` header. This can be useful if you are working with non-form content such as JSON and are working with an older web server and/or hosting provider that doesn't recognise particular HTTP methods such as `PATCH`. For example [Amazon Web Services ELB][aws_elb].
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.
To use it, make a `POST` request, setting the `X-HTTP-Method-Override` header.
For example:
For example, making a `PATCH` request via `POST` in jQuery:
<form action="/">
<input data-override='content-type' value='application/json' type='hidden'/>
<textarea data-override='content'>{}</textarea>
<input type="submit"/>
</form>
$.ajax({
url: '/myresource/',
method: 'POST',
headers: {'X-HTTP-Method-Override': 'PATCH'},
...
});
Note that prior to 3.3.0, this support was server-side rather than javascript based.
## Browser based submission of non-form content
## URL based format suffixes
Browser-based submission of content types other than form are supported by
using form fields named `_content` and `_content_type`:
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.
For example, given the following form:
This behavior is controlled using the `URL_FORMAT_OVERRIDE` setting.
<form action="/news-items/5" method="PUT">
<input type="hidden" name="_content_type" value="application/json">
<input name="_content" value="{'count': 1}">
</form>
## HTTP header based method overriding
`request.content_type` would return `"application/json"`, and
`request.stream` would return `"{'count': 1}"`
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.
## URL based accept headers
For example:
REST framework can take `?accept=application/json` style URL parameters,
which allow the `Accept` header to be overridden.
METHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE'
class MethodOverrideMiddleware(object):
def process_view(self, request, callback, callback_args, callback_kwargs):
if request.method != 'POST':
return
if METHOD_OVERRIDE_HEADER not in request.META:
return
request.method = request.META[METHOD_OVERRIDE_HEADER]
This can be useful for testing the API from a web browser, where you don't
have any control over what is sent in the `Accept` header.
## URL based accept headers
## URL based format suffixes
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.
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.
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:
This is a more concise than using the `accept` override, but it also gives
you less control. (For example you can't specify any media type parameters)
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?
......@@ -74,7 +81,7 @@ was later [dropped from the spec][html5]. There remains
as well as how to support content types other than form-encoded data.
[cite]: http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260
[ajax-form]: https://github.com/encode/ajax-form
[rails]: http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work
[html5]: http://www.w3.org/TR/html5-diff/#changes-2010-06-24
[put_delete]: http://amundsen.com/examples/put-delete-forms/
[aws_elb]: https://forums.aws.amazon.com/thread.jspa?messageID=400724
......@@ -50,7 +50,7 @@ Getting involved in triaging incoming issues is a good way to start contributing
To start developing on Django REST framework, clone the repo:
git clone git@github.com:tomchristie/django-rest-framework.git
git clone git@github.com:encode/django-rest-framework.git
Changes should broadly follow the [PEP 8][pep-8] style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles.
......@@ -61,6 +61,7 @@ To run the tests, clone the repository, and then:
# Setup the virtual environment
virtualenv env
source env/bin/activate
pip install django
pip install -r requirements.txt
# Run the tests
......@@ -201,11 +202,11 @@ If you want to draw attention to a note or warning, use a pair of enclosing line
[code-of-conduct]: https://www.djangoproject.com/conduct/
[google-group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[so-filter]: http://stackexchange.com/filters/66475/rest-framework
[issues]: https://github.com/tomchristie/django-rest-framework/issues?state=open
[issues]: https://github.com/encode/django-rest-framework/issues?state=open
[pep-8]: http://www.python.org/dev/peps/pep-0008/
[travis-status]: ../img/travis-status.png
[pull-requests]: https://help.github.com/articles/using-pull-requests
[tox]: http://tox.readthedocs.org/en/latest/
[tox]: https://tox.readthedocs.io/en/latest/
[markdown]: http://daringfireball.net/projects/markdown/basics
[docs]: https://github.com/tomchristie/django-rest-framework/tree/master/docs
[docs]: https://github.com/encode/django-rest-framework/tree/master/docs
[mou]: http://mouapp.com/
......@@ -4,11 +4,86 @@
>
> &mdash; Roy Fielding, [REST APIs must be hypertext driven][cite]
There are a variety of approaches to API documentation. This document introduces a few of the various tools and options you might choose from. The approaches should not be considered exclusive - you may want to provide more than one documentation style for you API, such as a self describing API that also includes static documentation of the various API endpoints.
REST framework provides built-in support for API documentation. There are also a number of great third-party documentation tools available.
## Endpoint documentation
## Built-in API documentation
The most common way to document Web APIs today is to produce documentation that lists the API endpoints verbatim, and describes the allowable operations on each. There are various tools that allow you to do this in an automated or semi-automated way.
The built-in API documentation includes:
* Documentation of API endpoints.
* Automatically generated code samples for each of the available API client libraries.
* Support for API interaction.
### Installation
The `coreapi` library is required as a dependancy for the API docs. Make sure
to install the latest version. The `pygments` and `markdown` libraries
are optional but recommended.
To install the API documentation, you'll need to include it in your projects URLconf:
from rest_framework.documentation import include_docs_urls
urlpatterns = [
...
url(r'^docs/', include_docs_urls(title='My API title'))
]
This will include two different views:
* `/docs/` - The documentation page itself.
* `/docs/schema.js` - A JavaScript resource that exposes the API schema.
### Documenting your views
You can document your views by including docstrings that describe each of the available actions.
For example:
class UserList(generics.ListAPIView):
"""
Return a list of all the existing users.
""""
If a view supports multiple methods, you should split your documentation using `method:` style delimiters.
class UserList(generics.ListCreateAPIView):
"""
get:
Return a list of all the existing users.
post:
Create a new user instance.
"""
When using viewsets, you should use the relevant action names as delimiters.
class UserViewSet(viewsets.ModelViewSet):
"""
retrieve:
Return the given user.
list:
Return a list of all the existing users.
create:
Create a new user instance.
"""
---
## Third party packages
There are a number of mature third-party packages for providing API documentation.
#### DRF Docs
[DRF Docs][drfdocs-repo] allows you to document Web APIs made with Django REST Framework and it is authored by Emmanouil Konstantinidis. It's made to work out of the box and its setup should not take more than a couple of minutes. Complete documentation can be found on the [website][drfdocs-website] while there is also a [demo][drfdocs-demo] available for people to see what it looks like. **Live API Endpoints** allow you to utilize the endpoints from within the documentation in a neat way.
Features include customizing the template with your branding, settings for hiding the docs depending on the environment and more.
Both this package and Django REST Swagger are fully documented, well supported, and come highly recommended.
![Screenshot - DRF docs][image-drf-docs]
---
......@@ -16,19 +91,41 @@ The most common way to document Web APIs today is to produce documentation that
Marc Gibbons' [Django REST Swagger][django-rest-swagger] integrates REST framework with the [Swagger][swagger] API documentation tool. The package produces well presented API documentation, and includes interactive tools for testing API endpoints.
The package is fully documented, well supported, and comes highly recommended.
Django REST Swagger supports REST framework versions 2.3 and above.
Mark is also the author of the [REST Framework Docs][rest-framework-docs] package which offers clean, simple autogenerated documentation for your API but is deprecated and has moved to Django REST Swagger.
Both this package and DRF docs are fully documented, well supported, and come highly recommended.
![Screenshot - Django REST Swagger][image-django-rest-swagger]
---
#### REST Framework Docs
### DRF AutoDocs
Oleksander Mashianovs' [DRF Auto Docs][drfautodocs-repo] automated api renderer.
Collects almost all the code you written into documentation effortlessly.
Supports:
The [REST Framework Docs][rest-framework-docs] package is an earlier project, also by Marc Gibbons, that offers clean, simple autogenerated documentation for your API.
* functional view docs
* tree-like structure
* Docstrings:
* markdown
* preserve space & newlines
* formatting with nice syntax
* Fields:
* choices rendering
* help_text (to specify SerializerMethodField output, etc)
* smart read_only/required rendering
* Endpoint properties:
* filter_backends
* authentication_classes
* permission_classes
* extra url params(GET params)
![Screenshot - REST Framework Docs][image-rest-framework-docs]
![whole structure](http://joxi.ru/52aBGNI4k3oyA0.jpg)
---
......@@ -71,7 +168,7 @@ If the python `markdown` library is installed, then [markdown syntax][markdown]
[ref]: http://example.com/activating-accounts
"""
Note that one constraint of using viewsets is that any documentation be used for all generated views, so for example, you cannot have differing documentation for the generated list view and detail view.
Note that when using viewsets the basic docstring is used for all generated views. To provide descriptions for each view, such as for the the list and retrieve views, use docstring sections as described in [Schemas as documentation: Examples][schemas-examples].
#### The `OPTIONS` method
......@@ -100,13 +197,18 @@ In this approach, rather than documenting the available API endpoints up front,
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]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven
[drfdocs-repo]: https://github.com/ekonstantinidis/django-rest-framework-docs
[drfdocs-website]: http://www.drfdocs.com/
[drfdocs-demo]: http://demo.drfdocs.com/
[drfautodocs-repo]: https://github.com/iMakedonsky/drf-autodocs
[django-rest-swagger]: https://github.com/marcgibbons/django-rest-swagger
[swagger]: https://developers.helloreverb.com/swagger/
[rest-framework-docs]: https://github.com/marcgibbons/django-rest-framework-docs
[apiary]: http://apiary.io/
[markdown]: http://daringfireball.net/projects/markdown/
[hypermedia-docs]: rest-hypermedia-hateoas.md
[image-drf-docs]: ../img/drfdocs.png
[image-django-rest-swagger]: ../img/django-rest-swagger.png
[image-rest-framework-docs]: ../img/rest-framework-docs.png
[image-apiary]: ../img/apiary.png
[image-self-describing-api]: ../img/self-describing.png
[schemas-examples]: ../api-guide/schemas/#examples
# HTML & Forms
REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can used as HTML forms and rendered in templates.
## Rendering HTML
In order to return HTML responses you'll need to 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**:
<html><body>
<h1>Profiles</h1>
<ul>
{% for profile in profiles %}
<li>{{ profile.name }}</li>
{% endfor %}
</ul>
</body></html>
## 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 %}
<html><body>
<h1>Profile - {{ profile.name }}</h1>
<form action="{% url 'profile-detail' pk=profile.pk %}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
</body></html>
### 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:
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
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 %}
...
<form action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/vertical' %}
<button type="submit" class="btn btn-default">Sign in</button>
</form>
![Vertical form example](../img/vertical.png)
---
#### `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 %}
...
<form class="form-horizontal" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form>
![Horizontal form example](../img/horizontal.png)
---
#### `rest_framework/inline`
A compact form style that presents all the controls inline.
{% load rest_framework %}
...
<form class="form-inline" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %}
{% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Sign in</button>
</form>
![Inline form example](../img/inline.png)
## 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
......@@ -81,7 +81,7 @@ If you're translating a new language you'll need to translate the existing REST
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
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.
......@@ -106,7 +106,7 @@ For API clients the most appropriate of these will typically be to use the `Acce
[django-translation]: https://docs.djangoproject.com/en/1.7/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/tomchristie/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.po
[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/1.7/topics/i18n/translation/#how-django-discovers-language-preference
[django-locale-paths]: https://docs.djangoproject.com/en/1.7/ref/settings/#std:setting-LOCALE_PATHS
[django-locale-name]: https://docs.djangoproject.com/en/1.7/topics/i18n/#term-locale-name
......
# Jobs
Looking for a new Django REST Framework related role? On this site we provide a list of job resources that may be helpful. It's also worth checking out if any of [our sponsors are hiring][drf-funding].
## Places to look for Django REST Framework Jobs
* [https://www.djangoproject.com/community/jobs/][djangoproject-website]
* [https://www.python.org/jobs/][python-org-jobs]
* [https://djangogigs.com][django-gigs-com]
* [https://djangojobs.net/jobs/][django-jobs-net]
* [http://djangojobbers.com][django-jobbers-com]
* [https://www.indeed.com/q-Django-jobs.html][indeed-com]
* [http://stackoverflow.com/jobs/developer-jobs-using-django][stackoverflow-com]
* [https://www.upwork.com/o/jobs/browse/skill/django-framework/][upwork-com]
* [https://www.technojobs.co.uk/django-jobs][technobjobs-co-uk]
* [https://remoteok.io/remote-django-jobs][remoteok-io]
* [https://www.remotepython.com/jobs/][remotepython-com]
Know of any other great resources for Django REST Framework jobs that are missing in our list? Please [submit a pull request][submit-pr] or [email us][anna-email].
Wonder how else you can help? One of the best ways you can help Django REST Framework is to ask interviewers if their company is signed up for [REST Framework sponsorship][drf-funding] yet.
[djangoproject-website]: https://www.djangoproject.com/community/jobs/
[python-org-jobs]: https://www.python.org/jobs/
[django-gigs-com]: https://djangogigs.com
[django-jobs-net]: https://djangojobs.net/jobs/
[django-jobbers-com]: http://djangojobbers.com
[indeed-com]: https://www.indeed.com/q-Django-jobs.html
[stackoverflow-com]: http://stackoverflow.com/jobs/developer-jobs-using-django
[upwork-com]: https://www.upwork.com/o/jobs/browse/skill/django-framework/
[technobjobs-co-uk]: https://www.technojobs.co.uk/django-jobs
[remoteok-io]: https://remoteok.io/remote-django-jobs
[remotepython-com]: https://www.remotepython.com/jobs/
[drf-funding]: https://fund.django-rest-framework.org/topics/funding/
[submit-pr]: https://github.com/encode/django-rest-framework
[anna-email]: mailto:anna@django-rest-framework.org
......@@ -102,7 +102,7 @@ Our gold sponsors include companies large and small. Many thanks for their signi
### Silver sponsors
The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank&nbsp;you to individuals who have choosen to privately support the project at this level.
The serious financial contribution that our silver sponsors have made is very much appreciated. I'd like to say a particular thank&nbsp;you to individuals who have chosen to privately support the project at this level.
<ul class="sponsor silver">
<li><a href="http://www.imtapps.com/" rel="nofollow" style="background-image:url(../../img/sponsors/3-imt_computer_services.png);">IMT Computer Services</a></li>
......
# Mozilla Grant
We have recently been [awarded a Mozilla grant](https://blog.mozilla.org/blog/2016/04/13/mozilla-open-source-support-moss-update-q1-2016/), in order to fund the next major releases of REST framework. This work will focus on seamless client-side integration by introducing supporting client libraries that are able to dynamically interact with REST framework APIs. The framework will provide for either hypermedia or schema endpoints, which will expose the available interface for the client libraries to interact with.
Additionally, we will be building on the realtime support that Django Channels provides, supporting and documenting how to build realtime APIs with REST framework. Again, this will include supporting work in the associated client libraries, making it easier to build richly interactive applications.
The [Core API](http://www.coreapi.org) project will provide the foundations for our client library support, and will allow us to support interaction using a wide range of schemas and hypermedia formats. It's worth noting that these client libraries won't be tightly coupled to solely REST framework APIs either, and will be able to interact with *any* API that exposes a supported schema or hypermedia format.
Specifically, the work includes:
## Client libraries
This work will include built-in schema and hypermedia support, allowing dynamic client libraries to interact with the API. I'll also be releasing both Python and Javascript client libraries, plus a command-line client, a new tutorial section, and further documentation.
* Client library support in REST framework.
* Schema & hypermedia support for REST framework APIs.
* A test client, allowing you to write tests that emulate a client library interacting with your API.
* New tutorial sections on using client libraries to interact with REST framework APIs.
* Python client library.
* JavaScript client library.
* Command line client.
## Realtime APIs
The next goal is to build on the realtime support offered by Django Channels, adding support & documentation for building realtime API endpoints.
* Support for API subscription endpoints, using REST framework and Django Channels.
* New tutorial section on building realtime API endpoints with REST framework.
* Realtime support in the Python & Javascript client libraries.
## Accountability
In order to ensure that I can be fully focused on trying to secure a sustainable
& well-funded open source business I will be leaving my current role at [DabApps](http://www.dabapps.com)
at the end of May 2016.
I have formed a UK limited company, [Encode](http://www.encode.io), which will
act as the business entity behind REST framework. I will be issuing monthly reports
from Encode on progress both towards the Mozilla grant, and for development time
funded via the [REST framework paid plans](funding.md).
<!-- Begin MailChimp Signup Form -->
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
<style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
/* Add your own MailChimp form style overrides in your site stylesheet or in this style block.
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
</style>
<div id="mc_embed_signup">
<form action="//encode.us13.list-manage.com/subscribe/post?u=b6b66bb5e4c7cb484a85c8dd7&amp;id=e382ef68ef" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h2>Stay up to date, with our monthly progress reports...</h2>
<div class="mc-field-group">
<label for="mce-EMAIL">Email Address </label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_b6b66bb5e4c7cb484a85c8dd7_e382ef68ef" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div>
</form>
</div>
<script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='LNAME';ftypes[2]='text';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
<!--End mc_embed_signup-->
......@@ -6,7 +6,7 @@
This document outlines our project management processes for REST framework.
The aim is to ensure that the project has a high
The aim is to ensure that the project has a high
["bus factor"][bus-factor], and can continue to remain well supported for the foreseeable future. Suggestions for improvements to our process are welcome.
---
......@@ -17,9 +17,9 @@ We have a quarterly maintenance cycle where new members may join the maintenance
#### Current team
The [maintenance team for Q1 2015](https://github.com/tomchristie/django-rest-framework/issues/2190):
The [maintenance team for Q4 2015](https://github.com/encode/django-rest-framework/issues/2190):
* [@tomchristie](https://github.com/tomchristie/)
* [@tomchristie](https://github.com/encode/)
* [@xordoquy](https://github.com/xordoquy/) (Release manager.)
* [@carltongibson](https://github.com/carltongibson/)
* [@kevin-brown](https://github.com/kevin-brown/)
......@@ -38,27 +38,27 @@ Members of the maintenance team will be added as collaborators to the repository
The following template should be used for the description of the issue, and serves as the formal process for selecting the team.
This issue is for determining the maintenance team for the *** period.
Please see the [Project management](http://www.django-rest-framework.org/topics/project-management/) section of our documentation for more details.
---
#### Renewing existing members.
The following people are the current maintenance team. Please checkmark your name if you wish to continue to have write permission on the repository for the *** period.
- [ ] @***
- [ ] @***
- [ ] @***
- [ ] @***
- [ ] @***
---
#### New members.
If you wish to be considered for this or a future date, please comment against this or subsequent issues.
To modify this process for future maintenance cycles make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation.
#### Responsibilities of team members
......@@ -104,9 +104,9 @@ The following template should be used for the description of the issue, and serv
Checklist:
- [ ] Create pull request for [release notes](https://github.com/tomchristie/django-rest-framework/blob/master/docs/topics/release-notes.md) based on the [*.*.* milestone](https://github.com/tomchristie/django-rest-framework/milestones/***).
- [ ] Create pull request for [release notes](https://github.com/encode/django-rest-framework/blob/master/docs/topics/release-notes.md) based on the [*.*.* milestone](https://github.com/encode/django-rest-framework/milestones/***).
- [ ] Update the translations from [transifex](http://www.django-rest-framework.org/topics/project-management/#translations).
- [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/__init__.py).
- [ ] Ensure the pull request increments the version to `*.*.*` in [`restframework/__init__.py`](https://github.com/encode/django-rest-framework/blob/master/rest_framework/__init__.py).
- [ ] Confirm with @tomchristie that release is finalized and ready to go.
- [ ] Ensure that release date is included in pull request.
- [ ] Merge the release pull request.
......@@ -116,7 +116,7 @@ The following template should be used for the description of the issue, and serv
- [ ] Make a release announcement on the [discussion group](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework).
- [ ] Make a release announcement on twitter.
- [ ] Close the milestone on GitHub.
To modify this process for future releases make a pull request to the [project management](http://www.django-rest-framework.org/topics/project-management/) documentation.
When pushing the release to PyPI ensure that your environment has been installed from our development `requirement.txt`, so that documentation and PyPI installs are consistently being built against a pinned set of packages.
......@@ -165,7 +165,7 @@ Here's how differences between the old and new source files will be handled:
When a translator has finished translating their work needs to be downloaded from Transifex into the REST framework repository. To do this, run:
# 3. Pull the translated django.po files from Transifex.
tx pull -a
tx pull -a --minimum-perc 10
cd rest_framework
# 4. Compile the binary .mo files for all supported languages.
django-admin.py compilemessages
......@@ -197,10 +197,10 @@ The following issues still need to be addressed:
* Document ownership and management of the security mailing list.
[bus-factor]: http://en.wikipedia.org/wiki/Bus_factor
[un-triaged]: https://github.com/tomchristie/django-rest-framework/issues?q=is%3Aopen+no%3Alabel
[un-triaged]: https://github.com/encode/django-rest-framework/issues?q=is%3Aopen+no%3Alabel
[transifex-project]: https://www.transifex.com/projects/p/django-rest-framework/
[transifex-client]: https://pypi.python.org/pypi/transifex-client
[translation-memory]: http://docs.transifex.com/guides/tm#let-tm-automatically-populate-translations
[github-org]: https://github.com/tomchristie/django-rest-framework/issues/2162
[github-org]: https://github.com/encode/django-rest-framework/issues/2162
[sandbox]: http://restframework.herokuapp.com/
[mailing-list]: https://groups.google.com/forum/#!forum/django-rest-framework
# Tutorials and Resources
There are a wide range of resources available for learning and using Django REST framework. We try to keep a comprehensive list available here.
## Books
<div class="book-covers">
<a class="book-cover" href="https://hellowebapp.com/order/">
<img src="../../img/books/hwa-cover.png"/>
</a>
<a class="book-cover" href="https://www.twoscoopspress.com/products/two-scoops-of-django-1-11">
<img src="../../img/books/tsd-cover.png"/>
</a>
</div>
## Tutorials
* [Beginner's Guide to the Django REST Framework][beginners-guide-to-the-django-rest-framework]
* [Django REST Framework - An Introduction][drf-an-intro]
* [Django REST Framework Tutorial][drf-tutorial]
* [Django REST Framework Course][django-rest-framework-course]
* [Building a RESTful API with Django REST Framework][building-a-restful-api-with-drf]
* [Getting Started with Django REST Framework and AngularJS][getting-started-with-django-rest-framework-and-angularjs]
* [End to End Web App with Django REST Framework & AngularJS][end-to-end-web-app-with-django-rest-framework-angularjs]
* [Start Your API - Django REST Framework Part 1][start-your-api-django-rest-framework-part-1]
* [Permissions & Authentication - Django REST Framework Part 2][permissions-authentication-django-rest-framework-part-2]
* [ViewSets and Routers - Django REST Framework Part 3][viewsets-and-routers-django-rest-framework-part-3]
* [Django REST Framework User Endpoint][django-rest-framework-user-endpoint]
* [Check Credentials Using Django REST Framework][check-credentials-using-django-rest-framework]
* [Creating a Production Ready API with Python and Django REST Framework – Part 1][creating-a-production-ready-api-with-python-and-drf-part1]
* [Creating a Production Ready API with Python and Django REST Framework – Part 2][creating-a-production-ready-api-with-python-and-drf-part2]
## Videos
### Talks
* [How to Make a Full Fledged REST API with Django OAuth Toolkit][full-fledged-rest-api-with-django-oauth-tookit]
* [Django REST API - So Easy You Can Learn It in 25 Minutes][django-rest-api-so-easy]
* [Tom Christie about Django Rest Framework at Django: Under The Hood][django-under-hood-2014]
* [Django REST Framework: Schemas, Hypermedia & Client Libraries][pycon-uk-2016]
### Tutorials
* [Django REST Framework Part 1][django-rest-framework-part-1-video]
* [Django REST Framework in Your PJ's!][drf-in-your-pjs]
* [Building a REST API Using Django & Django REST Framework][building-a-rest-api-using-django-and-drf]
* [Blog API with Django REST Framework][blog-api-with-drf]
* [Ember and Django Part 1][ember-and-django-part 1-video]
* [Django REST Framework Image Upload Tutorial (with AngularJS)][drf-image-upload-tutorial-with-angularjs]
* [Django REST Framework Tutorials][drf-tutorials]
## Articles
* [Web API performance: Profiling Django REST Framework][web-api-performance-profiling-django-rest-framework]
* [API Development with Django and Django REST Framework][api-development-with-django-and-django-rest-framework]
* [Integrating Pandas, Django REST Framework and Bokeh][integrating-pandas-drf-and-bokeh]
* [Controlling Uncertainty on Web Applications and APIs][controlling-uncertainty-on-web-apps-and-apis]
* [Full Text Search in Django REST Framework with Database Backends][full-text-search-in-drf]
* [OAuth2 Authentication with Django REST Framework and Custom Third-Party OAuth2 Backends][oauth2-authentication-with-drf]
* [Nested Resources with Django REST Framework][nested-resources-with-drf]
* [Image Fields with Django REST Framework][image-fields-with-drf]
* [Chatbot Using Django REST Framework + api.ai + Slack — Part 1/3][chatbot-using-drf-part1]
* [New Django Admin with DRF and EmberJS... What are the News?][new-django-admin-with-drf-and-emberjs]
* [Blog posts about Django REST Framework][medium-django-rest-framework]
### Documentations
* [Classy Django REST Framework][cdrf.co]
* [DRF-schema-adapter][drf-schema]
Want your Django REST Framework talk/tutorial/article to be added to our website? Or know of a resource that's not yet included here? Please [submit a pull request][submit-pr] or [email us][anna-email]!
[beginners-guide-to-the-django-rest-framework]: http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786
[getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
[end-to-end-web-app-with-django-rest-framework-angularjs]: http://mourafiq.com/2013/07/01/end-to-end-web-app-with-django-angular-1.html
[start-your-api-django-rest-framework-part-1]: https://godjango.com/41-start-your-api-django-rest-framework-part-1/
[permissions-authentication-django-rest-framework-part-2]: https://godjango.com/43-permissions-authentication-django-rest-framework-part-2/
[viewsets-and-routers-django-rest-framework-part-3]: https://godjango.com/45-viewsets-and-routers-django-rest-framework-part-3/
[django-rest-framework-user-endpoint]: http://richardtier.com/2014/02/25/django-rest-framework-user-endpoint/
[check-credentials-using-django-rest-framework]: http://richardtier.com/2014/03/06/110/
[ember-and-django-part 1-video]: http://www.neckbeardrepublic.com/screencasts/ember-and-django-part-1
[django-rest-framework-part-1-video]: http://www.neckbeardrepublic.com/screencasts/django-rest-framework-part-1
[web-api-performance-profiling-django-rest-framework]: http://dabapps.com/blog/api-performance-profiling-django-rest-framework/
[api-development-with-django-and-django-rest-framework]: https://bnotions.com/api-development-with-django-and-django-rest-framework/
[cdrf.co]:http://www.cdrf.co
[medium-django-rest-framework]: https://medium.com/django-rest-framework
[django-rest-framework-course]: https://teamtreehouse.com/library/django-rest-framework
[pycon-uk-2016]: https://www.youtube.com/watch?v=FjmiGh7OqVg
[django-under-hood-2014]: https://www.youtube.com/watch?v=3cSsbe-tA0E
[integrating-pandas-drf-and-bokeh]: http://machinalis.com/blog/pandas-django-rest-framework-bokeh/
[controlling-uncertainty-on-web-apps-and-apis]: http://machinalis.com/blog/controlling-uncertainty-on-web-applications-and-apis/
[full-text-search-in-drf]: http://machinalis.com/blog/full-text-search-on-django-rest-framework/
[oauth2-authentication-with-drf]: http://machinalis.com/blog/oauth2-authentication/
[nested-resources-with-drf]: http://machinalis.com/blog/nested-resources-with-django/
[image-fields-with-drf]: http://machinalis.com/blog/image-fields-with-django-rest-framework/
[chatbot-using-drf-part1]: https://chatbotslife.com/chatbot-using-django-rest-framework-api-ai-slack-part-1-3-69c7e38b7b1e#.g2aceuncf
[new-django-admin-with-drf-and-emberjs]: https://blog.levit.be/new-django-admin-with-emberjs-what-are-the-news/
[drf-schema]: http://drf-schema-adapter.readthedocs.io/en/latest/
[creating-a-production-ready-api-with-python-and-drf-part1]: https://www.andreagrandi.it/2016/09/28/creating-production-ready-api-python-django-rest-framework-part-1/
[creating-a-production-ready-api-with-python-and-drf-part2]: https://www.andreagrandi.it/2016/10/01/creating-a-production-ready-api-with-python-and-django-rest-framework-part-2/
[django-rest-api-so-easy]: https://www.youtube.com/watch?v=cqP758k1BaQ
[full-fledged-rest-api-with-django-oauth-tookit]: https://www.youtube.com/watch?v=M6Ud3qC2tTk
[drf-in-your-pjs]: https://www.youtube.com/watch?v=xMtHsWa72Ww
[building-a-rest-api-using-django-and-drf]: https://www.youtube.com/watch?v=PwssEec3IRw
[drf-tutorials]: https://www.youtube.com/watch?v=axRCBgbOJp8&list=PLJtp8Jm8EDzjgVg9vVyIUMoGyqtegj7FH
[drf-image-upload-tutorial-with-angularjs]: https://www.youtube.com/watch?v=hMiNTCIY7dw&list=PLUe5s-xycYk_X0vDjYBmKuIya2a2myF8O
[blog-api-with-drf]: https://www.youtube.com/watch?v=XMu0T6L2KRQ&list=PLEsfXFp6DpzTOcOVdZF-th7BS_GYGguAS
[drf-an-intro]: https://realpython.com/blog/python/django-rest-framework-quick-start/
[drf-tutorial]: https://tests4geeks.com/django-rest-framework-tutorial/
[building-a-restful-api-with-drf]: http://agiliq.com/blog/2014/12/building-a-restful-api-with-django-rest-framework/
[submit-pr]: https://github.com/encode/django-rest-framework
[anna-email]: mailto:anna@django-rest-framework.org
......@@ -25,7 +25,7 @@ Using numeric HTTP status codes in your views doesn't always make for obvious re
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.
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.
......@@ -96,7 +96,7 @@ Notice that we're no longer explicitly tying our requests or responses to a give
## 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].
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.
......@@ -186,6 +186,8 @@ Similarly, we can control the format of the request that we send, using the `Con
"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
......@@ -198,9 +200,9 @@ See the [browsable api][browsable-api] topic for more information about the brow
## 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.
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
[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
......
# Tutorial 3: Class Based Views
# 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].
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
## 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`.
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
......@@ -62,7 +62,7 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
That's looking good. Again, it's still pretty similar to the function based view right now.
We'll also need to refactor our `urls.py` slightly now we're using class based views.
We'll also need to refactor our `urls.py` slightly now that we're using class-based views.
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
......@@ -79,7 +79,7 @@ Okay, we're done. If you run the development server everything should be workin
## Using mixins
One of the big wins of using class based views is that it allows us to easily compose reusable bits of behaviour.
One of the big wins of using class-based views is that it allows us to easily compose reusable bits of behaviour.
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 behaviour are implemented in REST framework's mixin classes.
......@@ -124,7 +124,7 @@ The base class provides the core functionality, and the mixin classes provide th
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 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.
......
......@@ -14,7 +14,7 @@ First, let's add a couple of fields. One of those fields will be used to repres
Add the following two fields to the `Snippet` model in `models.py`.
owner = models.ForeignKey('auth.User', related_name='snippets')
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.
......@@ -67,7 +67,7 @@ Now that we've got some users to work with, we'd better add representations of t
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.
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
......@@ -146,11 +146,11 @@ And, at the end of the file, add a pattern to include the login and logout views
namespace='rest_framework')),
]
The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. The only restriction is that the included urls must use the `'rest_framework'` namespace.
The `r'^api-auth/'` part of pattern can actually be whatever URL you want to use. The only restriction is that the included urls must use the `'rest_framework'` namespace. In Django 1.9+, REST framework will set the namespace, so you may leave it out.
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 pks that are associated with each user, in each user's 'snippets' field.
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
......@@ -206,10 +206,10 @@ If we try to create a snippet without authenticating, we'll get an error:
We can make a successful request by including the username and password of one of the users we created earlier.
http -a tom:password POST http://127.0.0.1:8000/snippets/ code="print 789"
http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"
{
"id": 5,
"id": 1,
"owner": "tom",
"title": "foo",
"code": "print 789",
......
......@@ -11,14 +11,14 @@ Right now we have endpoints for 'snippets' and 'users', but we don't have a sing
from rest_framework.reverse import reverse
@api_view(('GET',))
@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`.
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
......@@ -67,7 +67,7 @@ In this case we'd like to use a hyperlinked style between entities. In order to
The `HyperlinkedModelSerializer` has the following differences from `ModelSerializer`:
* It does not include the `pk` field by default.
* It does not include the `id` field by default.
* It includes a `url` field, using `HyperlinkedIdentityField`.
* Relationships use `HyperlinkedRelatedField`,
instead of `PrimaryKeyRelatedField`.
......@@ -80,7 +80,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn
class Meta:
model = Snippet
fields = ('url', 'highlight', 'owner',
fields = ('url', 'id', 'highlight', 'owner',
'title', 'code', 'linenos', 'language', 'style')
......@@ -89,7 +89,7 @@ We can easily re-write our existing serializers to use hyperlinking. In your `sn
class Meta:
model = User
fields = ('url', 'username', 'snippets')
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.
......
......@@ -51,7 +51,7 @@ This time we've used the `ModelViewSet` class in order to get the complete set o
Notice that we've also used the `@detail_route` 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 `@detail_route` decorator will respond to `GET` requests. We can use the `methods` argument if we wanted an action that responded to `POST` requests.
Custom actions which use the `@detail_route` 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.
......@@ -130,27 +130,7 @@ Using viewsets can be a really useful abstraction. It helps ensure that URL con
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 views individually.
## Reviewing our work
In [part 7][tut-7] of the tutorial we'll look at how we can add an API schema,
and interact with our API using a client library or command line tool.
With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, and comes complete with authentication, per-object permissions, and multiple renderer formats.
We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.
You can review the final [tutorial code][repo] on GitHub, or try out a live example in [the sandbox][sandbox].
## Onwards and upwards
We've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:
* Contribute on [GitHub][github] by reviewing and submitting issues, and making pull requests.
* Join the [REST framework discussion group][group], and help build the community.
* Follow [the author][twitter] on Twitter and say hi.
**Now go build awesome things.**
[repo]: https://github.com/tomchristie/rest-framework-tutorial
[sandbox]: http://restframework.herokuapp.com/
[github]: https://github.com/tomchristie/django-rest-framework
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[twitter]: https://twitter.com/_tomchristie
[tut-7]: 7-schemas-and-client-libraries.md
# Tutorial 7: Schemas & client libraries
A schema is a machine-readable document that describes the available API
endpoints, their URLS, and what operations they support.
Schemas can be a useful tool for auto-generated documentation, and can also
be used to drive dynamic client libraries that can interact with the API.
## Core API
In order to provide schema support REST framework uses [Core API][coreapi].
Core API is a document specification for describing APIs. It is used to provide
an internal representation format of the available endpoints and possible
interactions that an API exposes. It can either be used server-side, or
client-side.
When used server-side, Core API allows an API to support rendering to a wide
range of schema or hypermedia formats.
When used client-side, Core API allows for dynamically driven client libraries
that can interact with any API that exposes a supported schema or hypermedia
format.
## Adding a schema
REST framework supports either explicitly defined schema views, or
automatically generated schemas. Since we're using viewsets and routers,
we can simply use the automatic schema generation.
You'll need to install the `coreapi` python package in order to include an
API schema.
$ pip install coreapi
We can now include a schema for our API, by including an autogenerated schema
view in our URL configuration.
```
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title='Pastebin API')
urlpatterns = [
   url(r'^schema/$', schema_view),
...
]
```
If you visit the API root endpoint in a browser you should now see `corejson`
representation become available as an option.
![Schema format](../img/corejson-format.png)
We can also request the schema from the command line, by specifying the desired
content type in the `Accept` header.
$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json
HTTP/1.0 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/coreapi+json
{
"_meta": {
"title": "Pastebin API"
},
"_type": "document",
...
The default output style is to use the [Core JSON][corejson] encoding.
Other schema formats, such as [Open API][openapi] (formerly Swagger) are
also supported.
## Using a command line client
Now that our API is exposing a schema endpoint, we can use a dynamic client
library to interact with the API. To demonstrate this, let's use the
Core API command line client.
The command line client is available as the `coreapi-cli` package:
$ pip install coreapi-cli
Now check that it is available on the command line...
$ coreapi
Usage: coreapi [OPTIONS] COMMAND [ARGS]...
Command line client for interacting with CoreAPI services.
Visit http://www.coreapi.org for more information.
Options:
--version Display the package version number.
--help Show this message and exit.
Commands:
...
First we'll load the API schema using the command line client.
$ coreapi get http://127.0.0.1:8000/schema/
<Pastebin API "http://127.0.0.1:8000/schema/">
snippets: {
highlight(id)
list()
read(id)
}
users: {
list()
read(id)
}
We haven't authenticated yet, so right now we're only able to see the read only
endpoints, in line with how we've set up the permissions on the API.
Let's try listing the existing snippets, using the command line client:
$ coreapi action snippets list
[
{
"url": "http://127.0.0.1:8000/snippets/1/",
"id": 1,
"highlight": "http://127.0.0.1:8000/snippets/1/highlight/",
"owner": "lucy",
"title": "Example",
"code": "print('hello, world!')",
"linenos": true,
"language": "python",
"style": "friendly"
},
...
Some of the API endpoints require named parameters. For example, to get back
the highlight HTML for a particular snippet we need to provide an id.
$ coreapi action snippets highlight --param id=1
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Example</title>
...
## Authenticating our client
If we want to be able to create, edit and delete snippets, we'll need to
authenticate as a valid user. In this case we'll just use basic auth.
Make sure to replace the `<username>` and `<password>` below with your
actual username and password.
$ coreapi credentials add 127.0.0.1 <username>:<password> --auth basic
Added credentials
127.0.0.1 "Basic <...>"
Now if we fetch the schema again, we should be able to see the full
set of available interactions.
$ coreapi reload
Pastebin API "http://127.0.0.1:8000/schema/">
snippets: {
create(code, [title], [linenos], [language], [style])
delete(id)
highlight(id)
list()
partial_update(id, [title], [code], [linenos], [language], [style])
read(id)
update(id, code, [title], [linenos], [language], [style])
}
users: {
list()
read(id)
}
We're now able to interact with these endpoints. For example, to create a new
snippet:
$ coreapi action snippets create --param title="Example" --param code="print('hello, world')"
{
"url": "http://127.0.0.1:8000/snippets/7/",
"id": 7,
"highlight": "http://127.0.0.1:8000/snippets/7/highlight/",
"owner": "lucy",
"title": "Example",
"code": "print('hello, world')",
"linenos": false,
"language": "python",
"style": "friendly"
}
And to delete a snippet:
$ coreapi action snippets delete --param id=7
As well as the command line client, developers can also interact with your
API using client libraries. The Python client library is the first of these
to be available, and a Javascript client library is planned to be released
soon.
For more details on customizing schema generation and using Core API
client libraries you'll need to refer to the full documentation.
## Reviewing our work
With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats.
We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.
You can review the final [tutorial code][repo] on GitHub, or try out a live example in [the sandbox][sandbox].
## Onwards and upwards
We've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:
* Contribute on [GitHub][github] by reviewing and submitting issues, and making pull requests.
* Join the [REST framework discussion group][group], and help build the community.
* Follow [the author][twitter] on Twitter and say hi.
**Now go build awesome things.**
[coreapi]: http://www.coreapi.org
[corejson]: http://www.coreapi.org/specification/encoding/#core-json-encoding
[openapi]: https://openapis.org/
[repo]: https://github.com/encode/rest-framework-tutorial
[sandbox]: http://restframework.herokuapp.com/
[github]: https://github.com/encode/django-rest-framework
[group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
[twitter]: https://twitter.com/_tomchristie
......@@ -28,7 +28,7 @@ Now sync your database for the first time:
python manage.py migrate
We'll also create an initial user named `admin` with a password of `password`. We'll authenticate as that user later in our example.
We'll also create an initial user named `admin` with a password of `password123`. We'll authenticate as that user later in our example.
python manage.py createsuperuser
......@@ -104,7 +104,7 @@ Okay, now let's wire up the API URLs. On to `tutorial/urls.py`...
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.
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.
......@@ -118,7 +118,9 @@ We'd also like to set a few global settings. We'd like to turn on pagination, a
)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAdminUser',
],
'PAGE_SIZE': 10
}
......@@ -130,11 +132,11 @@ Okay, we're done.
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
python manage.py runserver
We can now access our API, both from the command-line, using tools like `curl`...
bash: curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
bash: curl -H 'Accept: application/json; indent=4' -u admin:password123 http://127.0.0.1:8000/users/
{
"count": 2,
"next": null,
......@@ -157,7 +159,7 @@ We can now access our API, both from the command-line, using tools like `curl`..
Or using the [httpie][httpie], command line tool...
bash: http -a username:password http://127.0.0.1:8000/users/
bash: http -a admin:password123 http://127.0.0.1:8000/users/
HTTP/1.1 200 OK
...
......@@ -182,7 +184,7 @@ Or using the [httpie][httpie], command line tool...
}
Or directly through the browser...
Or directly through the browser, by going to the URL `http://127.0.0.1:8000/users/`...
![Quick start image][image]
......
{% extends "base.html" %}
{% extends "main.html" %}
{% block content %}
......
......@@ -413,3 +413,12 @@ ul.sponsor {
#mkdocs_search_modal article p{
word-wrap: break-word;
}
.toclink {
color: #333;
}
.book-cover img {
margin: 0 !important;
display: inline-block !important;
}
......@@ -4,11 +4,11 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>{% if page_title %}{{ page_title }} - {% endif %}{{ site_name }}</title>
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
<link href="{{ base_url }}/img/favicon.ico" rel="icon" type="image/x-icon">
<link rel="canonical" href="{{ canonical_url }}" />
<link rel="canonical" href="{{ page.canonical_url }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST{% if current_page %}, {{ current_page.title }}{% endif %}">
<meta name="description" content="Django, API, REST{% if page %}, {{ page.title }}{% endif %}">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
......@@ -38,15 +38,12 @@
</script>
<style>
span.fusion-wrap a {
display: block;
margin-top: 10px;
#sidebarInclude img {
margin-bottom: 10px;
}
#sidebarInclude a.promo {
color: black;
}
a.fusion-poweredby {
display: block;
margin-top: 10px;
}
}
@media (max-width: 767px) {
div.promo {
display: none;
......@@ -54,7 +51,7 @@
}
</style>
</head>
<body onload="prettyPrint()" class="{% if current_page and current_page.is_homepage %}index{% endif %}-page">
<body onload="prettyPrint()" class="{% if page and page.is_homepage %}index{% endif %}-page">
<div class="wrapper">
{% include "nav.html" %}
......@@ -86,14 +83,14 @@
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
{% if current_page and current_page.is_homepage %}
{% if page and page.is_homepage %}
<li class="main">
<a href="#">Django REST framework</a>
</li>
{% endif %}
{% for toc_item in toc %}
<li class="{% if current_page and not current_page.is_homepage %}main{% endif %}">
{% for toc_item in page.toc %}
<li class="{% if page and not page.is_homepage %}main{% endif %}">
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
</li>
......@@ -104,12 +101,10 @@
{% endfor %}
{% endfor %}
{% if current_page and current_page.is_homepage %}
<div class="promo">
<hr/>
<script type="text/javascript" src="//cdn.fusionads.net/fusion.js?zoneid=1332&serve=C6SDP2Y&placement=djangorestframework" id="_fusionads_js"></script>
<div id="sidebarInclude">
</div>
{% endif %}
</ul>
</div>
......@@ -117,15 +112,15 @@
<div id="main-content" class="span9">
{% block content %}
{% if meta.source %}
{% for filename in meta.source %}
<a class="github" href="https://github.com/tomchristie/django-rest-framework/tree/master/rest_framework/{{ filename }}">
{% if page.meta.source %}
{% for filename in page.meta.source %}
<a class="github" href="https://github.com/encode/django-rest-framework/tree/master/rest_framework/{{ filename }}">
<span class="label label-info">{{ filename }}</span>
</a>
{% endfor %}
{% endif %}
{{ content }}
{{ page.content }}
{% endblock %}
</div> <!--/span-->
......@@ -136,7 +131,7 @@
</div> <!--/.wrapper -->
<footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.</a>
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p>
</footer>
......@@ -146,6 +141,7 @@
<script src="{{ base_url }}/js/jquery-1.8.1-min.js"></script>
<script src="{{ base_url }}/js/prettify-1.0.js"></script>
<script src="{{ base_url }}/js/bootstrap-2.1.1-min.js"></script>
<script src="https://fund.django-rest-framework.org/sidebar_include.js"></script>
<script>var base_url = '{{ base_url }}';</script>
<script src="{{ base_url }}/mkdocs/js/require.js"></script>
<script src="{{ base_url }}/js/theme.js"></script>
......
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/tomchristie/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small {% if not next_page %}disabled{% endif %}" rel="prev" {% if next_page %}href="{{ next_page.url }}"{% endif %}>
<a class="repo-link btn btn-primary btn-small" href="https://github.com/encode/django-rest-framework/tree/master">GitHub</a>
<a class="repo-link btn btn-inverse btn-small {% if not page.next_page %}disabled{% endif %}" rel="prev" {% if page.next_page %}href="{{ page.next_page.url }}"{% endif %}>
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small {% if not previous_page %}disabled{% endif %}" rel="next" {% if previous_page %}href="{{ previous_page.url }}"{% endif %}>
<a class="repo-link btn btn-inverse btn-small {% if not page.previous_page %}disabled{% endif %}" rel="next" {% if page.previous_page %}href="{{ page.previous_page.url }}"{% endif %}>
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
......@@ -16,7 +16,7 @@
</a>
<a class="brand" href="http://www.django-rest-framework.org">Django REST framework</a>
<div class="nav-collapse collapse">
{% if include_nav %}
{% if nav|length>1 %}
<!-- Main navigation -->
<ul class="nav navbar-nav">
{% for nav_item in nav %} {% if nav_item.children %}
......
https://github.com/twbs/bootstrap/
The MIT License (MIT)
Copyright (c) 2011-2016 Twitter, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
https://github.com/bazh/jquery.json-view/
The MIT License (MIT)
Copyright (c) 2014 bazh. (http://github.com/bazh)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
......@@ -2,10 +2,14 @@ site_name: Django REST framework
site_url: http://www.django-rest-framework.org/
site_description: Django REST framework - Web APIs for Django
repo_url: https://github.com/tomchristie/django-rest-framework
repo_url: https://github.com/encode/django-rest-framework
theme_dir: docs_theme
markdown_extensions:
- toc:
anchorlink: True
pages:
- Home: 'index.md'
- Tutorial:
......@@ -16,6 +20,7 @@ pages:
- '4 - Authentication and permissions': 'tutorial/4-authentication-and-permissions.md'
- '5 - Relationships and hyperlinked APIs': 'tutorial/5-relationships-and-hyperlinked-apis.md'
- '6 - Viewsets and routers': 'tutorial/6-viewsets-and-routers.md'
- '7 - Schemas and client libraries': 'tutorial/7-schemas-and-client-libraries.md'
- API Guide:
- 'Requests': 'api-guide/requests.md'
- 'Responses': 'api-guide/responses.md'
......@@ -37,6 +42,7 @@ pages:
- 'Versioning': 'api-guide/versioning.md'
- 'Content negotiation': 'api-guide/content-negotiation.md'
- 'Metadata': 'api-guide/metadata.md'
- 'Schemas': 'api-guide/schemas.md'
- 'Format suffixes': 'api-guide/format-suffixes.md'
- 'Returning URLs': 'api-guide/reverse.md'
- 'Exceptions': 'api-guide/exceptions.md'
......@@ -45,16 +51,26 @@ pages:
- 'Settings': 'api-guide/settings.md'
- Topics:
- 'Documenting your API': 'topics/documenting-your-api.md'
- 'API Clients': 'topics/api-clients.md'
- 'Internationalization': 'topics/internationalization.md'
- 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md'
- 'HTML & Forms': 'topics/html-and-forms.md'
- 'Browser Enhancements': 'topics/browser-enhancements.md'
- 'The Browsable API': 'topics/browsable-api.md'
- 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md'
- 'Third Party Resources': 'topics/third-party-resources.md'
- 'Third Party Packages': 'topics/third-party-packages.md'
- 'Tutorials and Resources': 'topics/tutorials-and-resources.md'
- 'Contributing to REST framework': 'topics/contributing.md'
- 'Project management': 'topics/project-management.md'
- 'Jobs': 'topics/jobs.md'
- '3.0 Announcement': 'topics/3.0-announcement.md'
- '3.1 Announcement': 'topics/3.1-announcement.md'
- '3.2 Announcement': 'topics/3.2-announcement.md'
- '3.3 Announcement': 'topics/3.3-announcement.md'
- '3.4 Announcement': 'topics/3.4-announcement.md'
- '3.5 Announcement': 'topics/3.5-announcement.md'
- '3.6 Announcement': 'topics/3.6-announcement.md'
- 'Kickstarter Announcement': 'topics/kickstarter-announcement.md'
- 'Mozilla Grant': 'topics/mozilla-grant.md'
- 'Funding': 'topics/funding.md'
- 'Release Notes': 'topics/release-notes.md'
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment