Commit 9b30dab4 by Tom Christie

Merge branch 'restframework2' into rest-framework-2-merge

Conflicts:
	.gitignore
	.travis.yml
	AUTHORS
	README.rst
	djangorestframework/mixins.py
	djangorestframework/renderers.py
	djangorestframework/resources.py
	djangorestframework/serializer.py
	djangorestframework/templates/djangorestframework/base.html
	djangorestframework/templates/djangorestframework/login.html
	djangorestframework/templatetags/add_query_param.py
	djangorestframework/tests/accept.py
	djangorestframework/tests/authentication.py
	djangorestframework/tests/content.py
	djangorestframework/tests/reverse.py
	djangorestframework/tests/serializer.py
	djangorestframework/views.py
	docs/examples.rst
	docs/examples/blogpost.rst
	docs/examples/modelviews.rst
	docs/examples/objectstore.rst
	docs/examples/permissions.rst
	docs/examples/pygments.rst
	docs/examples/views.rst
	docs/howto/alternativeframeworks.rst
	docs/howto/mixin.rst
	docs/howto/reverse.rst
	docs/howto/usingurllib2.rst
	docs/index.rst
	docs/topics/release-notes.md
	examples/sandbox/views.py
	rest_framework/__init__.py
	rest_framework/compat.py
	rest_framework/utils/breadcrumbs.py
	setup.py
parents 7e5b1501 4e7805cb
......@@ -3,17 +3,11 @@
*~
.*
coverage.xml
env
docs/build
html
htmlcov
examples/media/pygments/[A-Za-z0-9]*
examples/media/objectstore/[A-Za-z0-9]*
build/*
dist/*
xmlrunner/*
djangorestframework.egg-info/*
html/
coverage/
build/
dist/
rest_framework.egg-info/
MANIFEST
!.gitignore
......
......@@ -5,22 +5,13 @@ python:
- "2.7"
env:
- DJANGO=https://github.com/django/django/zipball/master TESTS='python setup.py test'
- DJANGO=django==1.4.1 --use-mirrors TESTS='python setup.py test'
- DJANGO=django==1.3.3 --use-mirrors TESTS='python setup.py test'
- DJANGO=https://github.com/django/django/zipball/master
- DJANGO=django==1.4.1 --use-mirrors
- DJANGO=django==1.3.3 --use-mirrors
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
install:
- pip install $DJANGO
- pip install -e . --use-mirrors
- pip install -r requirements.txt
- pip install $DJANGO
- export PYTHONPATH=.
# command to run tests, e.g. python setup.py test
script:
- $TESTS
# Examples tests, currently dropped to keep the number of configurations sane
# - DJANGO=https://github.com/django/django/zipball/master TESTS='python examples/runtests.py'
# - DJANGO=django==1.4.1 --use-mirrors TESTS='python examples/runtests.py'
# - DJANGO=django==1.3.3 --use-mirrors TESTS='python examples/runtests.py'
# - pip install -r examples/requirements.txt
\ No newline at end of file
- python rest_framework/runtests/runtests.py
Tom Christie <tomchristie> - tom@tomchristie.com, @_tomchristie
Marko Tibold <markotibold> (Additional thanks for providing & managing the Jenkins CI Server)
Paul Bagwell <pbgwl>
Sébastien Piquemal <sebpiq>
Carmen Wick <cwick>
Alex Ehlke <aehlke>
Alen Mujezinovic <flashingpumpkin>
Carles Barrobés <txels>
Michael Fötsch <mfoetsch>
David Larlet <david>
Andrew Straw <astraw>
Zeth <zeth>
Fernando Zunino <fzunino>
Jens Alm <ulmus>
Craig Blaszczyk <jakul>
Garcia Solero <garciasolero>
Tom Drummond <devioustree>
Danilo Bargen <gwrtheyrn>
Andrew McCloud <amccloud>
Thomas Steinacher <thomasst>
Meurig Freeman <meurig>
Anthony Nemitz <anemitz>
Ewoud Kohl van Wijngaarden <ekohl>
Michael Ding <yandy>
Mjumbe Poe <mjumbewu>
Natim <natim>
Sebastian Żurek <sebzur>
Benoit C <dzen>
Chris Pickett <bunchesofdonald>
Ben Timby <btimby>
Michele Lazzeri <michelelazzeri-nextage>
Camille Harang <mammique>
Paul Oswald <poswald>
Sean C. Farley <scfarley>
Daniel Izquierdo <izquierdo>
Can Yavuz <tschan>
Shawn Lewis <shawnlewis>
Adam Ness <greylurk>
<yetist>
Max Arnold <max-arnold>
Ralph Broenink <ralphje>
Simon Pantzare <pilt>
Michael Barrett <phobologic>
THANKS TO:
Jesper Noehr <jespern> & the django-piston contributors for providing the starting point for this project.
And of course, to the Django core team and the Django community at large. You guys rock.
Copyright (c) 2011, Tom Christie
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
recursive-include djangorestframework/static *.ico *.txt *.css
recursive-include djangorestframework/templates *.txt *.html
recursive-include examples .keep *.py *.txt
recursive-include docs *.py *.rst *.html *.txt
include AUTHORS LICENSE CHANGELOG.rst requirements.txt tox.ini
recursive-include rest_framework/static *.js *.css *.png
recursive-include rest_framework/templates *.txt *.html
# Django REST framework
**A toolkit for building well-connected, self-describing web APIs.**
**Author:** Tom Christie. [Follow me on Twitter][twitter]
[![build-status-image]][travis]
---
**Full documentation for REST framework is available on [http://django-rest-framework.org][docs].**
Note that this is the 2.0 version of REST framework. If you are looking for earlier versions please see the [0.4.x branch][0.4] on GitHub.
---
# Overview
Django REST framework is a lightweight library that makes it easy to build Web APIs. It is designed as a modular and easy to customize architecture, based on Django's class based views.
Web APIs built using REST framework are fully self-describing and web browseable - a huge useability win for your developers. It also supports a wide range of media types, authentication and permission policies out of the box.
If you are considering using REST framework for your API, we recommend reading the [REST framework 2 announcment][rest-framework-2-announcement] which gives a good overview of the framework and it's capabilities.
There is also a sandbox API you can use for testing purposes, [available here][sandbox].
# Requirements
* Python (2.6, 2.7)
* Django (1.3, 1.4, 1.5)
**Optional:**
* [Markdown] - Markdown support for the self describing API.
* [PyYAML] - YAML content type support.
# Installation
Install using `pip`...
pip install djangorestframework
...or clone the project from github.
git clone git@github.com:tomchristie/django-rest-framework.git
pip install -r requirements.txt
# Development
To build the docs.
./mkdocs.py
To run the tests.
./rest_framework/runtests/runtests.py
# Changelog
## 2.0.0
* Redesign of core components.
* Fix **all of the things**.
# License
Copyright (c) 2011, Tom Christie
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[build-status-image]: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=restframework2
[travis]: http://travis-ci.org/tomchristie/django-rest-framework?branch=restframework2
[twitter]: https://twitter.com/_tomchristie
[0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X
[sandbox]: http://restframework.herokuapp.com/
[rest-framework-2-announcement]: topics/rest-framework-2-announcement.md
[docs]: http://tomchristie.github.com/django-rest-framework/
[urlobject]: https://github.com/zacharyvoase/urlobject
[markdown]: http://pypi.python.org/pypi/Markdown/
[pyyaml]: http://pypi.python.org/pypi/PyYAML
Django REST framework
=====================
**Django REST framework makes it easy to build well-connected, self-describing RESTful Web APIs.**
**Author:** Tom Christie. `Follow me on Twitter <https://twitter.com/_tomchristie>`_.
:build status: |build-image|
.. |build-image| image:: https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master
:target: https://secure.travis-ci.org/tomchristie/django-rest-framework
**Important!** REST framework 2.0 is due to be released to PyPI by the end of October.
If you are considering building a new API using REST framework you should start working
with the `restframework2` branch, and refering to the upcoming `REST framework 2 docs <http://tomchristie.github.com/django-rest-framework>`_.
Overview
========
Features:
* Creates awesome self-describing *web browse-able* APIs.
* Clean, modular design, using Django's class based views.
* Easily extended for custom content types, serialization formats and authentication policies.
* Stable, well tested code-base.
* Active developer community.
Full documentation for the project is available at http://django-rest-framework.org
Issue tracking is on `GitHub <https://github.com/tomchristie/django-rest-framework/issues>`_.
General questions should be taken to the `discussion group <http://groups.google.com/group/django-rest-framework>`_.
Requirements:
* Python 2.6+
* Django 1.3+
Installation Notes
==================
To clone the project from GitHub using git::
git clone git@github.com:tomchristie/django-rest-framework.git
To install django-rest-framework in a virtualenv environment::
cd django-rest-framework
virtualenv --no-site-packages --distribute env
source env/bin/activate
pip install -r requirements.txt # django, coverage
To run the tests::
export PYTHONPATH=. # Ensure djangorestframework is on the PYTHONPATH
python djangorestframework/runtests/runtests.py
To run the test coverage report::
export PYTHONPATH=. # Ensure djangorestframework is on the PYTHONPATH
python djangorestframework/runtests/runcoverage.py
To run the examples::
pip install -r examples/requirements.txt # pygments, httplib2, markdown
cd examples
export PYTHONPATH=..
python manage.py syncdb
python manage.py runserver
To build the documentation::
pip install -r docs/requirements.txt # sphinx
sphinx-build -c docs -b html -d docs/build docs html
To run the tests against the full set of supported configurations::
deactivate # Ensure we are not currently running in a virtualenv
tox
To create the sdist packages::
python setup.py sdist --formats=gztar,zip
"""
The :mod:`authentication` module provides a set of pluggable authentication classes.
Authentication behavior is provided by mixing the :class:`mixins.AuthMixin` class into a :class:`View` class.
The set of authentication methods which are used is then specified by setting the
:attr:`authentication` attribute on the :class:`View` class, and listing a set of :class:`authentication` classes.
"""
from django.contrib.auth import authenticate
from djangorestframework.compat import CsrfViewMiddleware
import base64
__all__ = (
'BaseAuthentication',
'BasicAuthentication',
'UserLoggedInAuthentication'
)
class BaseAuthentication(object):
"""
All authentication classes should extend BaseAuthentication.
"""
def __init__(self, view):
"""
:class:`Authentication` classes are always passed the current view on creation.
"""
self.view = view
def authenticate(self, request):
"""
Authenticate the :obj:`request` and return a :obj:`User` or :const:`None`. [*]_
.. [*] The authentication context *will* typically be a :obj:`User`,
but it need not be. It can be any user-like object so long as the
permissions classes (see the :mod:`permissions` module) on the view can
handle the object and use it to determine if the request has the required
permissions or not.
This can be an important distinction if you're implementing some token
based authentication mechanism, where the authentication context
may be more involved than simply mapping to a :obj:`User`.
"""
return None
class BasicAuthentication(BaseAuthentication):
"""
Use HTTP Basic authentication.
"""
def authenticate(self, request):
"""
Returns a :obj:`User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns :const:`None`.
"""
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
if 'HTTP_AUTHORIZATION' in request.META:
auth = request.META['HTTP_AUTHORIZATION'].split()
if len(auth) == 2 and auth[0].lower() == "basic":
try:
auth_parts = base64.b64decode(auth[1]).partition(':')
except TypeError:
return None
try:
uname, passwd = smart_unicode(auth_parts[0]), smart_unicode(auth_parts[2])
except DjangoUnicodeDecodeError:
return None
user = authenticate(username=uname, password=passwd)
if user is not None and user.is_active:
return user
return None
class UserLoggedInAuthentication(BaseAuthentication):
"""
Use Django's session framework for authentication.
"""
def authenticate(self, request):
"""
Returns a :obj:`User` if the request session currently has a logged in user.
Otherwise returns :const:`None`.
"""
self.view.DATA # Make sure our generic parsing runs first
if getattr(request, 'user', None) and request.user.is_active:
# Enforce CSRF validation for session based authentication.
resp = CsrfViewMiddleware().process_view(request, None, (), {})
if resp is None: # csrf passed
return request.user
return None
# TODO: TokenAuthentication, DigestAuthentication, OAuthAuthentication
"""
Django supports parsing the content of an HTTP request, but only for form POST requests.
That behavior is sufficient for dealing with standard HTML forms, but it doesn't map well
to general HTTP requests.
We need a method to be able to:
1.) Determine the parsed content on a request for methods other than POST (eg typically also PUT)
2.) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded
and multipart/form-data. (eg also handle multipart/json)
"""
from django.http import QueryDict
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError
from django.utils import simplejson as json
from djangorestframework import status
from djangorestframework.compat import yaml
from djangorestframework.response import ErrorResponse
from djangorestframework.utils.mediatypes import media_type_matches
from xml.etree import ElementTree as ET
from djangorestframework.compat import ETParseError
from xml.parsers.expat import ExpatError
import datetime
import decimal
__all__ = (
'BaseParser',
'JSONParser',
'PlainTextParser',
'FormParser',
'MultiPartParser',
'YAMLParser',
'XMLParser'
)
class BaseParser(object):
"""
All parsers should extend :class:`BaseParser`, specifying a :attr:`media_type` attribute,
and overriding the :meth:`parse` method.
"""
media_type = None
def __init__(self, view):
"""
Initialize the parser with the ``View`` instance as state,
in case the parser needs to access any metadata on the :obj:`View` object.
"""
self.view = view
def can_handle_request(self, content_type):
"""
Returns :const:`True` if this parser is able to deal with the given *content_type*.
The default implementation for this function is to check the *content_type*
argument against the :attr:`media_type` attribute set on the class to see if
they match.
This may be overridden to provide for other behavior, but typically you'll
instead want to just set the :attr:`media_type` attribute on the class.
"""
return media_type_matches(self.media_type, content_type)
def parse(self, stream):
"""
Given a *stream* to read from, return the deserialized output.
Should return a 2-tuple of (data, files).
"""
raise NotImplementedError("BaseParser.parse() Must be overridden to be implemented.")
class JSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be an object which is the parsed content of the response.
`files` will always be `None`.
"""
try:
return (json.load(stream), None)
except ValueError, exc:
raise ErrorResponse(status.HTTP_400_BAD_REQUEST,
{'detail': 'JSON parse error - %s' % unicode(exc)})
class YAMLParser(BaseParser):
"""
Parses YAML-serialized data.
"""
media_type = 'application/yaml'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be an object which is the parsed content of the response.
`files` will always be `None`.
"""
try:
return (yaml.safe_load(stream), None)
except (ValueError, yaml.parser.ParserError), exc:
content = {'detail': 'YAML parse error - %s' % unicode(exc)}
raise ErrorResponse(status.HTTP_400_BAD_REQUEST, content)
class PlainTextParser(BaseParser):
"""
Plain text parser.
"""
media_type = 'text/plain'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will simply be a string representing the body of the request.
`files` will always be `None`.
"""
return (stream.read(), None)
class FormParser(BaseParser):
"""
Parser for form data.
"""
media_type = 'application/x-www-form-urlencoded'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be a :class:`QueryDict` containing all the form parameters.
`files` will always be :const:`None`.
"""
data = QueryDict(stream.read())
return (data, None)
class MultiPartParser(BaseParser):
"""
Parser for multipart form data, which may include file data.
"""
media_type = 'multipart/form-data'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will be a :class:`QueryDict` containing all the form parameters.
`files` will be a :class:`QueryDict` containing all the form files.
"""
upload_handlers = self.view.request._get_upload_handlers()
try:
django_parser = DjangoMultiPartParser(self.view.request.META, stream, upload_handlers)
return django_parser.parse()
except MultiPartParserError, exc:
raise ErrorResponse(status.HTTP_400_BAD_REQUEST,
{'detail': 'multipart parse error - %s' % unicode(exc)})
class XMLParser(BaseParser):
"""
XML parser.
"""
media_type = 'application/xml'
def parse(self, stream):
"""
Returns a 2-tuple of `(data, files)`.
`data` will simply be a string representing the body of the request.
`files` will always be `None`.
"""
try:
tree = ET.parse(stream)
except (ExpatError, ETParseError, ValueError), exc:
content = {'detail': 'XML parse error - %s' % unicode(exc)}
raise ErrorResponse(status.HTTP_400_BAD_REQUEST, content)
data = self._xml_convert(tree.getroot())
return (data, None)
def _xml_convert(self, element):
"""
convert the xml `element` into the corresponding python object
"""
children = element.getchildren()
if len(children) == 0:
return self._type_convert(element.text)
else:
# if the fist child tag is list-item means all children are list-item
if children[0].tag == "list-item":
data = []
for child in children:
data.append(self._xml_convert(child))
else:
data = {}
for child in children:
data[child.tag] = self._xml_convert(child)
return data
def _type_convert(self, value):
"""
Converts the value returned by the XMl parse into the equivalent
Python type
"""
if value is None:
return value
try:
return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
except ValueError:
pass
try:
return int(value)
except ValueError:
pass
try:
return decimal.Decimal(value)
except decimal.InvalidOperation:
pass
return value
DEFAULT_PARSERS = (
JSONParser,
FormParser,
MultiPartParser,
XMLParser
)
if yaml:
DEFAULT_PARSERS += (YAMLParser, )
else:
YAMLParser = None
"""
The :mod:`permissions` module bundles a set of permission classes that are used
for checking if a request passes a certain set of constraints. You can assign a permission
class to your view by setting your View's :attr:`permissions` class attribute.
"""
from django.core.cache import cache
from djangorestframework import status
from djangorestframework.response import ErrorResponse
import time
__all__ = (
'BasePermission',
'FullAnonAccess',
'IsAuthenticated',
'IsAdminUser',
'IsUserOrIsAnonReadOnly',
'PerUserThrottling',
'PerViewThrottling',
'PerResourceThrottling'
)
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
_403_FORBIDDEN_RESPONSE = ErrorResponse(
status.HTTP_403_FORBIDDEN,
{'detail': 'You do not have permission to access this resource. ' +
'You may need to login or otherwise authenticate the request.'})
_503_SERVICE_UNAVAILABLE = ErrorResponse(
status.HTTP_503_SERVICE_UNAVAILABLE,
{'detail': 'request was throttled'})
class BasePermission(object):
"""
A base class from which all permission classes should inherit.
"""
def __init__(self, view):
"""
Permission classes are always passed the current view on creation.
"""
self.view = view
def check_permission(self, auth):
"""
Should simply return, or raise an :exc:`response.ErrorResponse`.
"""
pass
class FullAnonAccess(BasePermission):
"""
Allows full access.
"""
def check_permission(self, user):
pass
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def check_permission(self, user):
if not user.is_authenticated():
raise _403_FORBIDDEN_RESPONSE
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
def check_permission(self, user):
if not user.is_staff:
raise _403_FORBIDDEN_RESPONSE
class IsUserOrIsAnonReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def check_permission(self, user):
if (not user.is_authenticated() and
self.view.method not in SAFE_METHODS):
raise _403_FORBIDDEN_RESPONSE
class DjangoModelPermissions(BasePermission):
"""
The request is authenticated using `django.contrib.auth` permissions.
See: https://docs.djangoproject.com/en/dev/topics/auth/#permissions
It ensures that the user is authenticated, and has the appropriate
`add`/`change`/`delete` permissions on the model.
This permission should only be used on views with a `ModelResource`.
"""
# Map methods into required permission codes.
# Override this if you need to also provide 'read' permissions,
# or if you want to provide custom permission codes.
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
def get_required_permissions(self, method, model_cls):
"""
Given a model and an HTTP method, return the list of permission
codes that the user is required to have.
"""
kwargs = {
'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.module_name
}
try:
return [perm % kwargs for perm in self.perms_map[method]]
except KeyError:
ErrorResponse(status.HTTP_405_METHOD_NOT_ALLOWED)
def check_permission(self, user):
method = self.view.method
model_cls = self.view.resource.model
perms = self.get_required_permissions(method, model_cls)
if not user.is_authenticated or not user.has_perms(perms):
raise _403_FORBIDDEN_RESPONSE
class BaseThrottle(BasePermission):
"""
Rate throttling of requests.
The rate (requests / seconds) is set by a :attr:`throttle` attribute
on the :class:`.View` class. The attribute is a string of the form 'number of
requests/period'.
Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
Previous request information used for throttling is stored in the cache.
"""
attr_name = 'throttle'
default = '0/sec'
timer = time.time
def get_cache_key(self):
"""
Should return a unique cache-key which can be used for throttling.
Must be overridden.
"""
pass
def check_permission(self, auth):
"""
Check the throttling.
Return `None` or raise an :exc:`.ErrorResponse`.
"""
num, period = getattr(self.view, self.attr_name, self.default).split('/')
self.num_requests = int(num)
self.duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
self.auth = auth
self.check_throttle()
def check_throttle(self):
"""
Implement the check to see if the request should be throttled.
On success calls :meth:`throttle_success`.
On failure calls :meth:`throttle_failure`.
"""
self.key = self.get_cache_key()
self.history = cache.get(self.key, [])
self.now = self.timer()
# Drop any requests from the history which have now passed the
# throttle duration
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
self.throttle_failure()
else:
self.throttle_success()
def throttle_success(self):
"""
Inserts the current request's timestamp along with the key
into the cache.
"""
self.history.insert(0, self.now)
cache.set(self.key, self.history, self.duration)
header = 'status=SUCCESS; next=%s sec' % self.next()
self.view.add_header('X-Throttle', header)
def throttle_failure(self):
"""
Called when a request to the API has failed due to throttling.
Raises a '503 service unavailable' response.
"""
header = 'status=FAILURE; next=%s sec' % self.next()
self.view.add_header('X-Throttle', header)
raise _503_SERVICE_UNAVAILABLE
def next(self):
"""
Returns the recommended next request time in seconds.
"""
if self.history:
remaining_duration = self.duration - (self.now - self.history[-1])
else:
remaining_duration = self.duration
available_requests = self.num_requests - len(self.history) + 1
return '%.2f' % (remaining_duration / float(available_requests))
class PerUserThrottling(BaseThrottle):
"""
Limits the rate of API calls that may be made by a given user.
The user id will be used as a unique identifier if the user is
authenticated. For anonymous requests, the IP address of the client will
be used.
"""
def get_cache_key(self):
if self.auth.is_authenticated():
ident = self.auth.id
else:
ident = self.view.request.META.get('REMOTE_ADDR', None)
return 'throttle_user_%s' % ident
class PerViewThrottling(BaseThrottle):
"""
Limits the rate of API calls that may be used on a given view.
The class name of the view is used as a unique identifier to
throttle against.
"""
def get_cache_key(self):
return 'throttle_view_%s' % self.view.__class__.__name__
class PerResourceThrottling(BaseThrottle):
"""
Limits the rate of API calls that may be used against all views on
a given resource.
The class name of the resource is used as a unique identifier to
throttle against.
"""
def get_cache_key(self):
return 'throttle_resource_%s' % self.view.resource.__class__.__name__
"""
The :mod:`response` module provides Response classes you can use in your
views to return a certain HTTP response. Typically a response is *rendered*
into a HTTP response depending on what renderers are set on your view and
als depending on the accept header of the request.
"""
from django.core.handlers.wsgi import STATUS_CODE_TEXT
__all__ = ('Response', 'ErrorResponse')
# TODO: remove raw_content/cleaned_content and just use content?
class Response(object):
"""
An HttpResponse that may include content that hasn't yet been serialized.
"""
def __init__(self, status=200, content=None, headers=None):
self.status = status
self.media_type = None
self.has_content_body = content is not None
self.raw_content = content # content prior to filtering
self.cleaned_content = content # content after filtering
self.headers = headers or {}
@property
def status_text(self):
"""
Return reason text corresponding to our HTTP response status code.
Provided for convenience.
"""
return STATUS_CODE_TEXT.get(self.status, '')
class ErrorResponse(Exception):
"""
An exception representing an Response that should be returned immediately.
Any content should be serialized as-is, without being filtered.
"""
def __init__(self, status, content=None, headers={}):
self.response = Response(status, content=content, headers=headers)
"""
Customizable serialization.
"""
from django.db import models
from django.db.models.query import QuerySet, RawQuerySet
from django.utils.encoding import smart_unicode, is_protected_type, smart_str
import inspect
import types
# We register serializer classes, so that we can refer to them by their
# class names, if there are cyclical serialization heirachys.
_serializers = {}
def _field_to_tuple(field):
"""
Convert an item in the `fields` attribute into a 2-tuple.
"""
if isinstance(field, (tuple, list)):
return (field[0], field[1])
return (field, None)
def _fields_to_list(fields):
"""
Return a list of field tuples.
"""
return [_field_to_tuple(field) for field in fields or ()]
class _SkipField(Exception):
"""
Signals that a serialized field should be ignored.
We use this mechanism as the default behavior for ensuring
that we don't infinitely recurse when dealing with nested data.
"""
pass
class _RegisterSerializer(type):
"""
Metaclass to register serializers.
"""
def __new__(cls, name, bases, attrs):
# Build the class and register it.
ret = super(_RegisterSerializer, cls).__new__(cls, name, bases, attrs)
_serializers[name] = ret
return ret
class Serializer(object):
"""
Converts python objects into plain old native types suitable for
serialization. In particular it handles models and querysets.
The output format is specified by setting a number of attributes
on the class.
You may also override any of the serialization methods, to provide
for more flexible behavior.
Valid output types include anything that may be directly rendered into
json, xml etc...
"""
__metaclass__ = _RegisterSerializer
fields = ()
"""
Specify the fields to be serialized on a model or dict.
Overrides `include` and `exclude`.
"""
include = ()
"""
Fields to add to the default set to be serialized on a model/dict.
"""
exclude = ()
"""
Fields to remove from the default set to be serialized on a model/dict.
"""
rename = {}
"""
A dict of key->name to use for the field keys.
"""
related_serializer = None
"""
The default serializer class to use for any related models.
"""
depth = None
"""
The maximum depth to serialize to, or `None`.
"""
parent = None
"""
A reference to the root serializer when descending down into fields.
"""
def __init__(self, depth=None, stack=[], **kwargs):
if depth is not None:
self.depth = depth
self.stack = stack
def get_fields(self, obj):
fields = self.fields
# If `fields` is not set, we use the default fields and modify
# them with `include` and `exclude`
if not fields:
default = self.get_default_fields(obj)
include = self.include or ()
exclude = self.exclude or ()
fields = set(default + list(include)) - set(exclude)
return fields
def get_default_fields(self, obj):
"""
Return the default list of field names/keys for a model instance/dict.
These are used if `fields` is not given.
"""
if isinstance(obj, models.Model):
opts = obj._meta
return [field.name for field in opts.fields + opts.many_to_many]
else:
return obj.keys()
def get_related_serializer(self, info):
# If an element in `fields` is a 2-tuple of (str, tuple)
# then the second element of the tuple is the fields to
# set on the related serializer
class OnTheFlySerializer(self.__class__):
fields = info
parent = getattr(self, 'parent') or self
if isinstance(info, (list, tuple)):
return OnTheFlySerializer
# If an element in `fields` is a 2-tuple of (str, Serializer)
# then the second element of the tuple is the Serializer
# class to use for that field.
elif isinstance(info, type) and issubclass(info, Serializer):
return info
# If an element in `fields` is a 2-tuple of (str, str)
# then the second element of the tuple is the name of the Serializer
# class to use for that field.
#
# Black magic to deal with cyclical Serializer dependancies.
# Similar to what Django does for cyclically related models.
elif isinstance(info, str) and info in _serializers:
return _serializers[info]
# Otherwise use `related_serializer` or fall back to
# `OnTheFlySerializer` preserve custom serialization methods.
return getattr(self, 'related_serializer') or OnTheFlySerializer
def serialize_key(self, key):
"""
Keys serialize to their string value,
unless they exist in the `rename` dict.
"""
return self.rename.get(smart_str(key), smart_str(key))
def serialize_val(self, key, obj, related_info):
"""
Convert a model field or dict value into a serializable representation.
"""
related_serializer = self.get_related_serializer(related_info)
if self.depth is None:
depth = None
elif self.depth <= 0:
return self.serialize_max_depth(obj)
else:
depth = self.depth - 1
if obj in self.stack:
return self.serialize_recursion(obj)
else:
stack = self.stack[:]
stack.append(obj)
return related_serializer(depth=depth, stack=stack).serialize(
obj, request=getattr(self, 'request', None))
def serialize_max_depth(self, obj):
"""
Determine how objects should be serialized once `depth` is exceeded.
The default behavior is to ignore the field.
"""
raise _SkipField
def serialize_recursion(self, obj):
"""
Determine how objects should be serialized if recursion occurs.
The default behavior is to ignore the field.
"""
raise _SkipField
def serialize_model(self, instance):
"""
Given a model instance or dict, serialize it to a dict..
"""
data = {}
# Append the instance itself to the stack so that you never iterate
# back into the first object.
self.stack.append(instance)
fields = self.get_fields(instance)
# serialize each required field
for fname, related_info in _fields_to_list(fields):
try:
# we first check for a method 'fname' on self,
# 'fname's signature must be 'def fname(self, instance)'
meth = getattr(self, fname, None)
if (inspect.ismethod(meth) and
len(inspect.getargspec(meth)[0]) == 2):
obj = meth(instance)
elif hasattr(instance, '__contains__') and fname in instance:
# then check for a key 'fname' on the instance
obj = instance[fname]
elif hasattr(instance, smart_str(fname)):
# finally check for an attribute 'fname' on the instance
obj = getattr(instance, fname)
else:
continue
key = self.serialize_key(fname)
val = self.serialize_val(fname, obj, related_info)
data[key] = val
except _SkipField:
pass
return data
def serialize_iter(self, obj):
"""
Convert iterables into a serializable representation.
"""
return [self.serialize(item) for item in obj]
def serialize_func(self, obj):
"""
Convert no-arg methods and functions into a serializable representation.
"""
return self.serialize(obj())
def serialize_manager(self, obj):
"""
Convert a model manager into a serializable representation.
"""
return self.serialize_iter(obj.all())
def serialize_fallback(self, obj):
"""
Convert any unhandled object into a serializable representation.
"""
return smart_unicode(obj, strings_only=True)
def serialize(self, obj, request=None):
"""
Convert any object into a serializable representation.
"""
# Request from related serializer.
if request is not None:
self.request = request
if isinstance(obj, (dict, models.Model)):
# Model instances & dictionaries
return self.serialize_model(obj)
elif isinstance(obj, (tuple, list, set, QuerySet, RawQuerySet, types.GeneratorType)):
# basic iterables
return self.serialize_iter(obj)
elif isinstance(obj, models.Manager):
# Manager objects
return self.serialize_manager(obj)
elif inspect.isfunction(obj) and not inspect.getargspec(obj)[0]:
# function with no args
return self.serialize_func(obj)
elif inspect.ismethod(obj) and len(inspect.getargspec(obj)[0]) <= 1:
# bound method
return self.serialize_func(obj)
# Protected types are passed through as is.
# (i.e. Primitives like None, numbers, dates, and Decimals.)
if is_protected_type(obj):
return obj
# All other values are converted to string.
return self.serialize_fallback(obj)
{% autoescape off %}{{ name }}
{{ description }}
HTTP/1.0 {{ response.status }} {{ response.status_text }}
{% for key, val in response.headers.items %}{{ key }}: {{ val }}
{% endfor %}
{{ content }}{% endautoescape %}
{% load url from future %}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
{% load urlize_quoted_links %}
{% load add_query_param %}
{% load static %}
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href='{% get_static_prefix %}djangorestframework/css/style.css'/>
{% block extrastyle %}{% endblock %}
<title>{% block title %}Django REST framework - {{ name }}{% endblock %}</title>
{% block extrahead %}{% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE" />{% endblock %}
</head>
<body class="{% block bodyclass %}{% endblock %}">
<div id="container">
<div id="header">
<div id="branding">
<h1 id="site-name">{% block branding %}<a href='http://django-rest-framework.org'>Django REST framework</a> <span class="version"> v {{ version }}</span>{% endblock %}</h1>
</div>
<div id="user-tools">
{% block userlinks %}
{% if user.is_active %}
Welcome, {{ user }}.
<a href='{% url 'djangorestframework:logout' %}?next={{ request.path }}'>Log out</a>
{% else %}
Anonymous
<a href='{% url 'djangorestframework:login' %}?next={{ request.path }}'>Log in</a>
{% endif %}
{% endblock %}
</div>
{% block nav-global %}{% endblock %}
</div>
<div class="breadcrumbs">
{% block breadcrumbs %}
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
<a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a> {% if not forloop.last %}&rsaquo;{% endif %}
{% endfor %}
{% endblock %}
</div>
<!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}">
{% if 'OPTIONS' in view.allowed_methods %}
<form action="{{ request.get_full_path }}" method="post">
{% csrf_token %}
<input type="hidden" name="{{ METHOD_PARAM }}" value="OPTIONS" />
<input type="submit" value="OPTIONS" class="default" />
</form>
{% endif %}
<div class='content-main'>
<h1>{{ name }}</h1>
<p>{{ description }}</p>
<div class='module'>
<pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %}
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
{% endfor %}
{{ content|urlize_quoted_links }}</pre>{% endautoescape %}</div>
{% if 'GET' in view.allowed_methods %}
<form>
<fieldset class='module aligned'>
<h2>GET {{ name }}</h2>
<div class='submit-row' style='margin: 0; border: 0'>
<a href='{{ request.get_full_path }}' rel="nofollow" style='float: left'>GET</a>
{% for format in available_formats %}
{% with FORMAT_PARAM|add:"="|add:format as param %}
[<a href='{{ request.get_full_path|add_query_param:param }}' rel="nofollow">{{ format }}</a>]
{% endwith %}
{% endfor %}
</div>
</fieldset>
</form>
{% endif %}
{# Only display the POST/PUT/DELETE forms if method tunneling via POST forms is enabled and the user has permissions on this view. #}
{% if METHOD_PARAM and response.status != 403 %}
{% if 'POST' in view.allowed_methods %}
<form action="{{ request.get_full_path }}" method="post" {% if post_form.is_multipart %}enctype="multipart/form-data"{% endif %}>
<fieldset class='module aligned'>
<h2>POST {{ name }}</h2>
{% csrf_token %}
{{ post_form.non_field_errors }}
{% for field in post_form %}
<div class='form-row'>
{{ field.label_tag }}
{{ field }}
<span class='help'>{{ field.help_text }}</span>
{{ field.errors }}
</div>
{% endfor %}
<div class='submit-row' style='margin: 0; border: 0'>
<input type="submit" value="POST" class="default" />
</div>
</fieldset>
</form>
{% endif %}
{% if 'PUT' in view.allowed_methods %}
<form action="{{ request.get_full_path }}" method="post" {% if put_form.is_multipart %}enctype="multipart/form-data"{% endif %}>
<fieldset class='module aligned'>
<h2>PUT {{ name }}</h2>
<input type="hidden" name="{{ METHOD_PARAM }}" value="PUT" />
{% csrf_token %}
{{ put_form.non_field_errors }}
{% for field in put_form %}
<div class='form-row'>
{{ field.label_tag }}
{{ field }}
<span class='help'>{{ field.help_text }}</span>
{{ field.errors }}
</div>
{% endfor %}
<div class='submit-row' style='margin: 0; border: 0'>
<input type="submit" value="PUT" class="default" />
</div>
</fieldset>
</form>
{% endif %}
{% if 'DELETE' in view.allowed_methods %}
<form action="{{ request.get_full_path }}" method="post">
<fieldset class='module aligned'>
<h2>DELETE {{ name }}</h2>
{% csrf_token %}
<input type="hidden" name="{{ METHOD_PARAM }}" value="DELETE" />
<div class='submit-row' style='margin: 0; border: 0'>
<input type="submit" value="DELETE" class="default" />
</div>
</fieldset>
</form>
{% endif %}
{% endif %}
</div>
<!-- END content-main -->
</div>
<!-- END Content -->
{% block footer %}<div id="footer"></div>{% endblock %}
</div>
</body>
</html>
{% load static %}
{% load url from future %}
<html>
<head>
<link rel="stylesheet" type="text/css" href='{% get_static_prefix %}djangorestframework/css/style.css'/>
</head>
<body class="login">
<div id="container">
<div id="header">
<div id="branding">
<h1 id="site-name">Django REST framework</h1>
</div>
</div>
<div id="content" class="colM">
<div id="content-main">
<form method="post" action="{% url 'djangorestframework:login' %}" id="login-form">
{% csrf_token %}
<div class="form-row">
<label for="id_username">Username:</label> {{ form.username }}
</div>
<div class="form-row">
<label for="id_password">Password:</label> {{ form.password }}
<input type="hidden" name="next" value="{{ next }}" />
</div>
<div class="form-row">
<label>&nbsp;</label><input type="submit" value="Log in">
</div>
</form>
<script type="text/javascript">
document.getElementById('id_username').focus()
</script>
</div>
<br class="clear">
</div>
<div id="footer"></div>
</div>
</body>
</html>
from django.template import Library
from urlobject import URLObject
register = Library()
def add_query_param(url, param):
return unicode(URLObject(url).add_query_param(*param.split('=')))
register.filter('add_query_param', add_query_param)
\ No newline at end of file
from django.conf.urls.defaults import patterns, url, include
from django.test import TestCase
from djangorestframework.compat import RequestFactory
from djangorestframework.views import View
# See: http://www.useragentstring.com/
MSIE_9_USER_AGENT = 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US))'
MSIE_8_USER_AGENT = 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)'
MSIE_7_USER_AGENT = 'Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)'
FIREFOX_4_0_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)'
CHROME_11_0_USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/11.0.655.0 Safari/534.17'
SAFARI_5_0_USER_AGENT = 'Mozilla/5.0 (X11; U; Linux x86_64; en-ca) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+'
OPERA_11_0_MSIE_USER_AGENT = 'Mozilla/4.0 (compatible; MSIE 8.0; X11; Linux x86_64; pl) Opera 11.00'
OPERA_11_0_OPERA_USER_AGENT = 'Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00'
urlpatterns = patterns('',
url(r'^api', include('djangorestframework.urls', namespace='djangorestframework'))
)
class UserAgentMungingTest(TestCase):
"""
We need to fake up the accept headers when we deal with MSIE. Blergh.
http://www.gethifi.com/blog/browser-rest-http-accept-headers
"""
urls = 'djangorestframework.tests.accept'
def setUp(self):
class MockView(View):
permissions = ()
def get(self, request):
return {'a':1, 'b':2, 'c':3}
self.req = RequestFactory()
self.MockView = MockView
self.view = MockView.as_view()
def test_munge_msie_accept_header(self):
"""Send MSIE user agent strings and ensure that we get an HTML response,
even if we set a */* accept header."""
for user_agent in (MSIE_9_USER_AGENT,
MSIE_8_USER_AGENT,
MSIE_7_USER_AGENT):
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
resp = self.view(req)
self.assertEqual(resp['Content-Type'], 'text/html')
def test_dont_munge_msie_with_x_requested_with_header(self):
"""Send MSIE user agent strings, and an X-Requested-With header, and
ensure that we get a JSON response if we set a */* Accept header."""
for user_agent in (MSIE_9_USER_AGENT,
MSIE_8_USER_AGENT,
MSIE_7_USER_AGENT):
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
resp = self.view(req)
self.assertEqual(resp['Content-Type'], 'application/json')
def test_dont_rewrite_msie_accept_header(self):
"""Turn off _IGNORE_IE_ACCEPT_HEADER, send MSIE user agent strings and ensure
that we get a JSON response if we set a */* accept header."""
view = self.MockView.as_view(_IGNORE_IE_ACCEPT_HEADER=False)
for user_agent in (MSIE_9_USER_AGENT,
MSIE_8_USER_AGENT,
MSIE_7_USER_AGENT):
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
resp = view(req)
self.assertEqual(resp['Content-Type'], 'application/json')
def test_dont_munge_nice_browsers_accept_header(self):
"""Send Non-MSIE user agent strings and ensure that we get a JSON response,
if we set a */* Accept header. (Other browsers will correctly set the Accept header)"""
for user_agent in (FIREFOX_4_0_USER_AGENT,
CHROME_11_0_USER_AGENT,
SAFARI_5_0_USER_AGENT,
OPERA_11_0_MSIE_USER_AGENT,
OPERA_11_0_OPERA_USER_AGENT):
req = self.req.get('/', HTTP_ACCEPT='*/*', HTTP_USER_AGENT=user_agent)
resp = self.view(req)
self.assertEqual(resp['Content-Type'], 'application/json')
from django.test import TestCase
from django import forms
from djangorestframework.compat import RequestFactory
from djangorestframework.views import View
from djangorestframework.resources import FormResource
import StringIO
class UploadFilesTests(TestCase):
"""Check uploading of files"""
def setUp(self):
self.factory = RequestFactory()
def test_upload_file(self):
class FileForm(forms.Form):
file = forms.FileField()
class MockView(View):
permissions = ()
form = FileForm
def post(self, request, *args, **kwargs):
return {'FILE_NAME': self.CONTENT['file'].name,
'FILE_CONTENT': self.CONTENT['file'].read()}
file = StringIO.StringIO('stuff')
file.name = 'stuff.txt'
request = self.factory.post('/', {'file': file})
view = MockView.as_view()
response = view(request)
self.assertEquals(response.content, '{"FILE_CONTENT": "stuff", "FILE_NAME": "stuff.txt"}')
from django.test import TestCase
from djangorestframework.compat import RequestFactory
from djangorestframework.mixins import RequestMixin
class TestMethodOverloading(TestCase):
def setUp(self):
self.req = RequestFactory()
def test_standard_behaviour_determines_GET(self):
"""GET requests identified"""
view = RequestMixin()
view.request = self.req.get('/')
self.assertEqual(view.method, 'GET')
def test_standard_behaviour_determines_POST(self):
"""POST requests identified"""
view = RequestMixin()
view.request = self.req.post('/')
self.assertEqual(view.method, 'POST')
def test_overloaded_POST_behaviour_determines_overloaded_method(self):
"""POST requests can be overloaded to another method by setting a reserved form field"""
view = RequestMixin()
view.request = self.req.post('/', {view._METHOD_PARAM: 'DELETE'})
self.assertEqual(view.method, 'DELETE')
def test_HEAD_is_a_valid_method(self):
"""HEAD requests identified"""
view = RequestMixin()
view.request = self.req.head('/')
self.assertEqual(view.method, 'HEAD')
from django.db import models
from django.contrib.auth.models import Group
class CustomUser(models.Model):
"""
A custom user model, which uses a 'through' table for the foreign key
"""
username = models.CharField(max_length=255, unique=True)
groups = models.ManyToManyField(
to=Group, blank=True, null=True, through='UserGroupMap'
)
@models.permalink
def get_absolute_url(self):
return ('custom_user', (), {
'pk': self.id
})
class UserGroupMap(models.Model):
user = models.ForeignKey(to=CustomUser)
group = models.ForeignKey(to=Group)
@models.permalink
def get_absolute_url(self):
return ('user_group_map', (), {
'pk': self.id
})
from django.conf.urls.defaults import patterns, url
from django.forms import ModelForm
from django.contrib.auth.models import Group, User
from djangorestframework.resources import ModelResource
from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from djangorestframework.tests.models import CustomUser
from djangorestframework.tests.testcases import TestModelsTestCase
class GroupResource(ModelResource):
model = Group
class UserForm(ModelForm):
class Meta:
model = User
exclude = ('last_login', 'date_joined')
class UserResource(ModelResource):
model = User
form = UserForm
class CustomUserResource(ModelResource):
model = CustomUser
urlpatterns = patterns('',
url(r'^users/$', ListOrCreateModelView.as_view(resource=UserResource), name='users'),
url(r'^users/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=UserResource)),
url(r'^customusers/$', ListOrCreateModelView.as_view(resource=CustomUserResource), name='customusers'),
url(r'^customusers/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=CustomUserResource)),
url(r'^groups/$', ListOrCreateModelView.as_view(resource=GroupResource), name='groups'),
url(r'^groups/(?P<id>[0-9]+)/$', InstanceModelView.as_view(resource=GroupResource)),
)
class ModelViewTests(TestModelsTestCase):
"""Test the model views djangorestframework provides"""
urls = 'djangorestframework.tests.modelviews'
def test_creation(self):
"""Ensure that a model object can be created"""
self.assertEqual(0, Group.objects.count())
response = self.client.post('/groups/', {'name': 'foo'})
self.assertEqual(response.status_code, 201)
self.assertEqual(1, Group.objects.count())
self.assertEqual('foo', Group.objects.all()[0].name)
def test_creation_with_m2m_relation(self):
"""Ensure that a model object with a m2m relation can be created"""
group = Group(name='foo')
group.save()
self.assertEqual(0, User.objects.count())
response = self.client.post('/users/', {'username': 'bar', 'password': 'baz', 'groups': [group.id]})
self.assertEqual(response.status_code, 201)
self.assertEqual(1, User.objects.count())
user = User.objects.all()[0]
self.assertEqual('bar', user.username)
self.assertEqual('baz', user.password)
self.assertEqual(1, user.groups.count())
group = user.groups.all()[0]
self.assertEqual('foo', group.name)
def test_creation_with_m2m_relation_through(self):
"""
Ensure that a model object with a m2m relation can be created where that
relation uses a through table
"""
group = Group(name='foo')
group.save()
self.assertEqual(0, User.objects.count())
response = self.client.post('/customusers/', {'username': 'bar', 'groups': [group.id]})
self.assertEqual(response.status_code, 201)
self.assertEqual(1, CustomUser.objects.count())
user = CustomUser.objects.all()[0]
self.assertEqual('bar', user.username)
self.assertEqual(1, user.groups.count())
group = user.groups.all()[0]
self.assertEqual('foo', group.name)
"""Tests for the djangorestframework package setup."""
from django.test import TestCase
import djangorestframework
class TestVersion(TestCase):
"""Simple sanity test to check the VERSION exists"""
def test_version(self):
"""Ensure the VERSION exists."""
djangorestframework.VERSION
# Right now we expect this test to fail - I'm just going to leave it commented out.
# Looking forward to actually being able to raise ExpectedFailure sometime!
#
#from django.test import TestCase
#from djangorestframework.response import Response
#
#
#class TestResponse(TestCase):
#
# # Interface tests
#
# # This is mainly to remind myself that the Response interface needs to change slightly
# def test_response_interface(self):
# """Ensure the Response interface is as expected."""
# response = Response()
# getattr(response, 'status')
# getattr(response, 'content')
# getattr(response, 'headers')
from django.conf.urls.defaults import patterns, url
from django.test import TestCase
from django.utils import simplejson as json
from djangorestframework.renderers import JSONRenderer
from djangorestframework.reverse import reverse, reverse_lazy
from djangorestframework.views import View
class ReverseView(View):
"""
Mock resource which simply returns a URL, so that we can ensure
that reversed URLs are fully qualified.
"""
renderers = (JSONRenderer, )
def get(self, request):
return reverse('reverse', request=request)
class LazyView(View):
"""
Mock resource which simply returns a URL, so that we can ensure
that reversed URLs are fully qualified.
"""
renderers = (JSONRenderer, )
def get(self, request):
return reverse_lazy('lazy', request=request)
urlpatterns = patterns('',
url(r'^reverse$', ReverseView.as_view(), name='reverse'),
url(r'^lazy$', LazyView.as_view(), name='lazy'),
)
class ReverseTests(TestCase):
"""
Tests for fully qualifed URLs when using `reverse`.
"""
urls = 'djangorestframework.tests.reverse'
def test_reversed_urls_are_fully_qualified(self):
response = self.client.get('/reverse')
self.assertEqual(json.loads(response.content),
'http://testserver/reverse')
#def test_reversed_lazy_urls_are_fully_qualified(self):
# response = self.client.get('/lazy')
# self.assertEqual(json.loads(response.content),
# 'http://testserver/lazy')
"""Tests for the resource module"""
from django.db import models
from django.test import TestCase
from django.utils.translation import ugettext_lazy
from djangorestframework.serializer import Serializer
import datetime
import decimal
class TestObjectToData(TestCase):
"""
Tests for the Serializer class.
"""
def setUp(self):
self.serializer = Serializer()
self.serialize = self.serializer.serialize
def test_decimal(self):
"""Decimals need to be converted to a string representation."""
self.assertEquals(self.serialize(decimal.Decimal('1.5')), decimal.Decimal('1.5'))
def test_function(self):
"""Functions with no arguments should be called."""
def foo():
return 1
self.assertEquals(self.serialize(foo), 1)
def test_method(self):
"""Methods with only a ``self`` argument should be called."""
class Foo(object):
def foo(self):
return 1
self.assertEquals(self.serialize(Foo().foo), 1)
def test_datetime(self):
"""datetime objects are left as-is."""
now = datetime.datetime.now()
self.assertEquals(self.serialize(now), now)
def test_dict_method_name_collision(self):
"""dict with key that collides with dict method name"""
self.assertEquals(self.serialize({'items': 'foo'}), {'items': u'foo'})
self.assertEquals(self.serialize({'keys': 'foo'}), {'keys': u'foo'})
self.assertEquals(self.serialize({'values': 'foo'}), {'values': u'foo'})
def test_ugettext_lazy(self):
self.assertEquals(self.serialize(ugettext_lazy('foobar')), u'foobar')
class TestFieldNesting(TestCase):
"""
Test nesting the fields in the Serializer class
"""
def setUp(self):
self.serializer = Serializer()
self.serialize = self.serializer.serialize
class M1(models.Model):
field1 = models.CharField(max_length=256)
field2 = models.CharField(max_length=256)
class M2(models.Model):
field = models.OneToOneField(M1)
class M3(models.Model):
field = models.ForeignKey(M1)
self.m1 = M1(field1='foo', field2='bar')
self.m2 = M2(field=self.m1)
self.m3 = M3(field=self.m1)
def test_tuple_nesting(self):
"""
Test tuple nesting on `fields` attr
"""
class SerializerM2(Serializer):
fields = (('field', ('field1',)),)
class SerializerM3(Serializer):
fields = (('field', ('field2',)),)
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
def test_serializer_class_nesting(self):
"""
Test related model serialization
"""
class NestedM2(Serializer):
fields = ('field1', )
class NestedM3(Serializer):
fields = ('field2', )
class SerializerM2(Serializer):
fields = [('field', NestedM2)]
class SerializerM3(Serializer):
fields = [('field', NestedM3)]
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
# def test_serializer_no_fields(self):
# """
# Test related serializer works when the fields attr isn't present. Fix for
# #178.
# """
# class NestedM2(Serializer):
# fields = ('field1', )
# class NestedM3(Serializer):
# fields = ('field2', )
# class SerializerM2(Serializer):
# include = [('field', NestedM2)]
# exclude = ('id', )
# class SerializerM3(Serializer):
# fields = [('field', NestedM3)]
# self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
# self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
def test_serializer_classname_nesting(self):
"""
Test related model serialization
"""
class SerializerM2(Serializer):
fields = [('field', 'NestedM2')]
class SerializerM3(Serializer):
fields = [('field', 'NestedM3')]
class NestedM2(Serializer):
fields = ('field1', )
class NestedM3(Serializer):
fields = ('field2', )
self.assertEqual(SerializerM2().serialize(self.m2), {'field': {'field1': u'foo'}})
self.assertEqual(SerializerM3().serialize(self.m3), {'field': {'field2': u'bar'}})
def test_serializer_overridden_hook_method(self):
"""
Test serializing a model instance which overrides a class method on the
serializer. Checks for correct behaviour in odd edge case.
"""
class SerializerM2(Serializer):
fields = ('overridden', )
def overridden(self):
return False
self.m2.overridden = True
self.assertEqual(SerializerM2().serialize_model(self.m2),
{'overridden': True})
from django.core.urlresolvers import reverse
from django.conf.urls.defaults import patterns, url, include
from django.http import HttpResponse
from django.test import TestCase
from django.test import Client
from django import forms
from django.db import models
from djangorestframework.views import View
from djangorestframework.parsers import JSONParser
from djangorestframework.resources import ModelResource
from djangorestframework.views import ListOrCreateModelView, InstanceModelView
from StringIO import StringIO
class MockView(View):
"""This is a basic mock view"""
pass
class MockViewFinal(View):
"""View with final() override"""
def final(self, request, response, *args, **kwargs):
return HttpResponse('{"test": "passed"}', content_type="application/json")
class ResourceMockView(View):
"""This is a resource-based mock view"""
class MockForm(forms.Form):
foo = forms.BooleanField(required=False)
bar = forms.IntegerField(help_text='Must be an integer.')
baz = forms.CharField(max_length=32)
form = MockForm
class MockResource(ModelResource):
"""This is a mock model-based resource"""
class MockResourceModel(models.Model):
foo = models.BooleanField()
bar = models.IntegerField(help_text='Must be an integer.')
baz = models.CharField(max_length=32, help_text='Free text. Max length 32 chars.')
model = MockResourceModel
fields = ('foo', 'bar', 'baz')
urlpatterns = patterns('',
url(r'^mock/$', MockView.as_view()),
url(r'^mock/final/$', MockViewFinal.as_view()),
url(r'^resourcemock/$', ResourceMockView.as_view()),
url(r'^model/$', ListOrCreateModelView.as_view(resource=MockResource)),
url(r'^model/(?P<pk>[^/]+)/$', InstanceModelView.as_view(resource=MockResource)),
url(r'^restframework/', include('djangorestframework.urls', namespace='djangorestframework')),
)
class BaseViewTests(TestCase):
"""Test the base view class of djangorestframework"""
urls = 'djangorestframework.tests.views'
def test_view_call_final(self):
response = self.client.options('/mock/final/')
self.assertEqual(response['Content-Type'].split(';')[0], "application/json")
parser = JSONParser(None)
(data, files) = parser.parse(StringIO(response.content))
self.assertEqual(data['test'], 'passed')
def test_options_method_simple_view(self):
response = self.client.options('/mock/')
self._verify_options_response(response,
name='Mock',
description='This is a basic mock view')
def test_options_method_resource_view(self):
response = self.client.options('/resourcemock/')
self._verify_options_response(response,
name='Resource Mock',
description='This is a resource-based mock view',
fields={'foo':'BooleanField',
'bar':'IntegerField',
'baz':'CharField',
})
def test_options_method_model_resource_list_view(self):
response = self.client.options('/model/')
self._verify_options_response(response,
name='Mock List',
description='This is a mock model-based resource',
fields={'foo':'BooleanField',
'bar':'IntegerField',
'baz':'CharField',
})
def test_options_method_model_resource_detail_view(self):
response = self.client.options('/model/0/')
self._verify_options_response(response,
name='Mock Instance',
description='This is a mock model-based resource',
fields={'foo':'BooleanField',
'bar':'IntegerField',
'baz':'CharField',
})
def _verify_options_response(self, response, name, description, fields=None, status=200,
mime_type='application/json'):
self.assertEqual(response.status_code, status)
self.assertEqual(response['Content-Type'].split(';')[0], mime_type)
parser = JSONParser(None)
(data, files) = parser.parse(StringIO(response.content))
self.assertTrue('application/json' in data['renders'])
self.assertEqual(name, data['name'])
self.assertEqual(description, data['description'])
if fields is None:
self.assertFalse(hasattr(data, 'fields'))
else:
self.assertEqual(data['fields'], fields)
class ExtraViewsTests(TestCase):
"""Test the extra views djangorestframework provides"""
urls = 'djangorestframework.tests.views'
def test_login_view(self):
"""Ensure the login view exists"""
response = self.client.get(reverse('djangorestframework:login'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
def test_logout_view(self):
"""Ensure the logout view exists"""
response = self.client.get(reverse('djangorestframework:logout'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response['Content-Type'].split(';')[0], 'text/html')
# TODO: Add login/logout behaviour tests
from django.conf.urls.defaults import patterns, url
template_name = {'template_name': 'djangorestframework/login.html'}
urlpatterns = patterns('django.contrib.auth.views',
url(r'^login/$', 'login', template_name, name='login'),
url(r'^logout/$', 'logout', template_name, name='logout'),
)
<a class="github" href="authentication.py"></a>
# Authentication
> Auth needs to be pluggable.
>
> &mdash; Jacob Kaplan-Moss, ["REST worst practices"][cite]
Authentication is the mechanism of associating an incoming request with a set of identifying credentials, such as the user the request came from, or the token that it was signed with. The [permission] and [throttling] policies can then use those credentials to determine if the request should be permitted.
REST framework provides a number of authentication policies out of the box, and also allows you to implement custom policies.
Authentication will run the first time either the `request.user` or `request.auth` properties are accessed, and determines how those properties are initialized.
The `request.user` property will typically be set to an instance of the `contrib.auth` package's `User` class.
The `request.auth` property is used for any additional authentication information, for example, it may be used to represent an authentication token that the request was signed with.
## How authentication is determined
The authentication policy is always defined as a list of classes. REST framework will attempt to authenticate with each class in the list, and will set `request.user` and `request.auth` using the return value of the first class that successfully authenticates.
If no class authenticates, `request.user` will be set to an instance of `django.contrib.auth.models.AnonymousUser`, and `request.auth` will be set to `None`.
The value of `request.user` and `request.auth` for unauthenticated requests can be modified using the `UNAUTHENTICATED_USER` and `UNAUTHENTICATED_TOKEN` settings.
## Setting the authentication policy
The default authentication policy may be set globally, using the `DEFAULT_AUTHENTICATION_CLASSES` setting. For example.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.UserBasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
You can also set the authentication policy on a per-view basis, using the `APIView` class based views.
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, UserBasicAuthentication)
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
Or, if you're using the `@api_view` decorator with function based views.
@api_view(['GET'])
@authentication_classes((SessionAuthentication, UserBasicAuthentication))
@permissions_classes((IsAuthenticated,))
def example_view(request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
# API Reference
## BasicAuthentication
This policy uses [HTTP Basic Authentication][basicauth], signed against a user's username and password. Basic authentication is generally only appropriate for testing.
If successfully authenticated, `BasicAuthentication` provides the following credentials.
* `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be `None`.
**Note:** If you use `BasicAuthentication` in production you must ensure that your API is only available over `https` only. You should also ensure that your API clients will always re-request the username and password at login, and will never store those details to persistent storage.
## TokenAuthentication
This policy uses a simple token-based HTTP Authentication scheme. Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.
To use the `TokenAuthentication` policy, include `rest_framework.authtoken` in your `INSTALLED_APPS` setting.
You'll also need to create tokens for your users.
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...)
print token.key
For clients to authenticate, the token key should be included in the `Authorization` HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
If successfully authenticated, `TokenAuthentication` provides the following credentials.
* `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be a `rest_framework.tokenauth.models.BasicToken` instance.
**Note:** If you use `TokenAuthentication` in production you must ensure that your API is only available over `https` only.
## OAuthAuthentication
This policy uses the [OAuth 2.0][oauth] protocol to authenticate requests. OAuth is appropriate for server-server setups, such as when you want to allow a third-party service to access your API on a user's behalf.
If successfully authenticated, `OAuthAuthentication` provides the following credentials.
* `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be a `rest_framework.models.OAuthToken` instance.
## SessionAuthentication
This policy uses Django's default session backend for authentication. Session authentication is appropriate for AJAX clients that are running in the same session context as your website.
If successfully authenticated, `SessionAuthentication` provides the following credentials.
* `request.user` will be a `django.contrib.auth.models.User` instance.
* `request.auth` will be `None`.
# Custom authentication
To implement a custom authentication policy, subclass `BaseAuthentication` and override the `.authenticate(self, request)` method. The method should return a two-tuple of `(user, auth)` if authentication succeeds, or `None` otherwise.
[cite]: http://jacobian.org/writing/rest-worst-practices/
[basicauth]: http://tools.ietf.org/html/rfc2617
[oauth]: http://oauth.net/2/
[permission]: permissions.md
[throttling]: throttling.md
<a class="github" href="negotiation.py"></a>
# Content negotiation
> HTTP has provisions for several mechanisms for "content negotiation" - the process of selecting the best representation for a given response when there are multiple representations available.
>
> &mdash; [RFC 2616][cite], Fielding et al.
[cite]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html
Content negotiation is the process of selecting one of multiple possible representations to return to a client, based on client or server preferences.
## Determining the accepted renderer
REST framework uses a simple style of content negotiation to determine which media type should be returned to a client, based on the available renderers, the priorities of each of those renderers, and the client's `Accept:` header. The style used is partly client-driven, and partly server-driven.
1. More specific media types are given preference to less specific media types.
2. If multiple media types have the same specificity, then preference is given to based on the ordering of the renderers configured for the given view.
For example, given the following `Accept` header:
application/json; indent=4, application/json, application/yaml, text/html, */*
The priorities for each of the given media types would be:
* `application/json; indent=4`
* `application/json`, `application/yaml` and `text/html`
* `*/*`
If the requested view was only configured with renderers for `YAML` and `HTML`, then REST framework would select whichever renderer was listed first in the `renderer_classes` list or `DEFAULT_RENDERER_CLASSES` setting.
For more information on the `HTTP Accept` header, see [RFC 2616][accept-header]
---
**Note**: "q" values are not taken into account by REST framework when determining preference. The use of "q" values negatively impacts caching, and in the author's opinion they are an unnecessary and overcomplicated approach to content negotiation.
This is a valid approach as the HTTP spec deliberately underspecifies how a server should weight server-based preferences against client-based preferences.
---
# Custom content negotiation
It's unlikely that you'll want to provide a custom content negotiation scheme for REST framework, but you can do so if needed. To implement a custom content negotiation scheme override `BaseContentNegotiation`.
REST framework's content negotiation classes handle selection of both the appropriate parser for the request, and the appropriate renderer for the response, so you should implement both the `.select_parser(request, parsers)` and `.select_renderer(request, renderers, format_suffix)` methods.
## Example
The following is a custom content negotiation class which ignores the client
request when selecting the appropriate parser or renderer.
class IgnoreClientContentNegotiation(BaseContentNegotiation):
def select_parser(self, request, parsers):
"""
Select the first parser in the `.parser_classes` list.
"""
return parsers[0]
def select_renderer(self, request, renderers, format_suffix):
"""
Select the first renderer in the `.renderer_classes` list.
"""
return renderers[0]
[accept-header]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
<a class="github" href="exceptions.py"></a>
# Exceptions
> Exceptions… allow error handling to be organized cleanly in a central or high-level place within the program structure.
>
> &mdash; Doug Hellmann, [Python Exception Handling Techniques][cite]
## Exception handling in REST framework views
REST framework's views handle various exceptions, and deal with returning appropriate error responses.
The handled exceptions are:
* Subclasses of `APIException` raised inside REST framework.
* Django's `Http404` exception.
* Django's `PermissionDenied` exception.
In each case, REST framework will return a response with an appropriate status code and content-type. The body of the response will include any additional details regarding the nature of the error.
By default all error responses will include a key `details` in the body of the response, but other keys may also be included.
For example, the following request:
DELETE http://api.example.com/foo/bar HTTP/1.1
Accept: application/json
Might receive an error response indicating that the `DELETE` method is not allowed on that resource:
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json; charset=utf-8
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
---
# API Reference
## APIException
**Signature:** `APIException(detail=None)`
The **base class** for all exceptions raised inside REST framework.
To provide a custom exception, subclass `APIException` and set the `.status_code` and `.detail` properties on the class.
## ParseError
**Signature:** `ParseError(detail=None)`
Raised if the request contains malformed data when accessing `request.DATA` or `request.FILES`.
By default this exception results in a response with the HTTP status code "400 Bad Request".
## PermissionDenied
**Signature:** `PermissionDenied(detail=None)`
Raised when an incoming request fails the permission checks.
By default this exception results in a response with the HTTP status code "403 Forbidden".
## MethodNotAllowed
**Signature:** `MethodNotAllowed(method, detail=None)`
Raised when an incoming request occurs that does not map to a handler method on the view.
By default this exception results in a response with the HTTP status code "405 Method Not Allowed".
## UnsupportedMediaType
**Signature:** `UnsupportedMediaType(media_type, detail=None)`
Raised if there are no parsers that can handle the content type of the request data when accessing `request.DATA` or `request.FILES`.
By default this exception results in a response with the HTTP status code "415 Unsupported Media Type".
## Throttled
**Signature:** `Throttled(wait=None, detail=None)`
Raised when an incoming request fails the throttling checks.
By default this exception results in a response with the HTTP status code "429 Too Many Requests".
[cite]: http://www.doughellmann.com/articles/how-tos/python-exception-handling/index.html
<a class="github" href="fields.py"></a>
# Serializer fields
> Flat is better than nested.
>
> &mdash; [The Zen of Python][cite]
Serializer fields handle converting between primative values and internal datatypes. They also deal with validating input values, as well as retrieving and setting the values from their parent objects.
---
**Note:** The serializer fields are declared in fields.py, but by convention you should import them using `from rest_framework import serializers` and refer to fields as `serializers.<FieldName>`.
---
## Core arguments
Each serializer field class constructor takes at least these arguments. Some Field classes take additional, field-specific arguments, but the following should always be accepted:
### `source`
The name of the attribute that will be used to populate the field. May be a method that only takes a `self` argument, such as `Field(source='get_absolute_url')`, or may use dotted notation to traverse attributes, such as `Field(source='user.email')`.
The value `source='*'` has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations. (See the implementation of the `PaginationSerializer` class for an example.)
Defaults to the name of the field.
### `read_only`
Set this to `True` to ensure that the field is used when serializing a representation, but is not used when updating an instance dureing deserialization.
Defaults to `False`
### `required`
Normally an error will be raised if a field is not supplied during deserialization.
Set to false if this field is not required to be present during deserialization.
Defaults to `True`.
### `default`
If set, this gives the default value that will be used for the field if none is supplied. If not set the default behaviour is to not populate the attribute at all.
### `validators`
A list of Django validators that should be used to validate deserialized values.
### `error_messages`
A dictionary of error codes to error messages.
### `widget`
Used only if rendering the field to HTML.
This argument sets the widget that should be used to render the field.
---
# Generic Fields
These generic fields are used for representing arbitrary model fields or the output of model methods.
## Field
A generic, **read-only** field. You can use this field for any attribute that does not need to support write operations.
For example, using the following model.
class Account(models.Model):
owner = models.ForeignKey('auth.user')
name = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
payment_expiry = models.DateTimeField()
def has_expired(self):
now = datetime.datetime.now()
return now > self.payment_expiry
A serializer definition that looked like this:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
expired = Field(source='has_expired')
class Meta:
fields = ('url', 'owner', 'name', 'expired')
Would produce output similar to:
{
'url': 'http://example.com/api/accounts/3/',
'owner': 'http://example.com/api/users/12/',
'name': 'FooCorp business account',
'expired': True
}
By default, the `Field` class will perform a basic translation of the source value into primative datatypes, falling back to unicode representations of complex datatypes when necessary.
You can customize this behaviour by overriding the `.to_native(self, value)` method.
## WritableField
A field that supports both read and write operations. By itself `WriteableField` does not perform any translation of input values into a given type. You won't typically use this field directly, but you may want to override it and implement the `.to_native(self, value)` and `.from_native(self, value)` methods.
## ModelField
A generic field that can be tied to any arbitrary model field. The `ModelField` class delegates the task of serialization/deserialization to it's associated model field. This field can be used to create serializer fields for custom model fields, without having to create a new custom serializer field.
**Signature:** `ModelField(model_field=<Django ModelField class>)`
---
# Typed Fields
These fields represent basic datatypes, and support both reading and writing values.
## BooleanField
A Boolean representation.
Corresponds to `django.db.models.fields.BooleanField`.
## CharField
A text representation, optionally validates the text to be shorter than `max_length` and longer than `min_length`.
Corresponds to `django.db.models.fields.CharField`
or `django.db.models.fields.TextField`.
**Signature:** `CharField(max_length=None, min_length=None)`
## ChoiceField
A field that can accept a value out of a limited set of choices.
## EmailField
A text representation, validates the text to be a valid e-mail address.
Corresponds to `django.db.models.fields.EmailField`
## DateField
A date representation.
Corresponds to `django.db.models.fields.DateField`
## DateTimeField
A date and time representation.
Corresponds to `django.db.models.fields.DateTimeField`
## IntegerField
An integer representation.
Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` and `django.db.models.fields.PositiveSmallIntegerField`
## FloatField
A floating point representation.
Corresponds to `django.db.models.fields.FloatField`.
---
# Relational Fields
Relational fields are used to represent model relationships. They can be applied to `ForeignKey`, `ManyToManyField` and `OneToOneField` relationships, as well as to reverse relationships, and custom relationships such as `GenericForeignKey`.
## RelatedField
This field can be applied to any of the following:
* A `ForeignKey` field.
* A `OneToOneField` field.
* A reverse OneToOne relationship
* Any other "to-one" relationship.
By default `RelatedField` will represent the target of the field using it's `__unicode__` method.
You can customise this behaviour by subclassing `ManyRelatedField`, and overriding the `.to_native(self, value)` method.
## ManyRelatedField
This field can be applied to any of the following:
* A `ManyToManyField` field.
* A reverse ManyToMany relationship.
* A reverse ForeignKey relationship
* Any other "to-many" relationship.
By default `ManyRelatedField` will represent the targets of the field using their `__unicode__` method.
For example, given the following models:
class TaggedItem(models.Model):
"""
Tags arbitrary model instances using a generic relation.
See: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
"""
tag = models.SlugField()
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __unicode__(self):
return self.tag
class Bookmark(models.Model):
"""
A bookmark consists of a URL, and 0 or more descriptive tags.
"""
url = models.URLField()
tags = GenericRelation(TaggedItem)
And a model serializer defined like this:
class BookmarkSerializer(serializers.ModelSerializer):
tags = serializers.ManyRelatedField(source='tags')
class Meta:
model = Bookmark
exclude = ('id',)
Then an example output format for a Bookmark instance would be:
{
'tags': [u'django', u'python'],
'url': u'https://www.djangoproject.com/'
}
## PrimaryKeyRelatedField
This field can be applied to any "to-one" relationship, such as a `ForeignKey` field.
`PrimaryKeyRelatedField` will represent the target of the field using it's primary key.
Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag.
## ManyPrimaryKeyRelatedField
This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship.
`PrimaryKeyRelatedField` will represent the targets of the field using their primary key.
Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag.
## HyperlinkedRelatedField
This field can be applied to any "to-one" relationship, such as a `ForeignKey` field.
`HyperlinkedRelatedField` will represent the target of the field using a hyperlink. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink.
Be default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag.
## ManyHyperlinkedRelatedField
This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship.
`ManyHyperlinkedRelatedField` will represent the targets of the field using hyperlinks. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink.
Be default, `ManyHyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag.
## HyperLinkedIdentityField
This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer.
You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the model.
This field is always read-only.
[cite]: http://www.python.org/dev/peps/pep-0020/
<a class="github" href="urlpatterns.py"></a>
# Format suffixes
> Section 6.2.1 does not say that content negotiation should be
used all the time.
>
> &mdash; Roy Fielding, [REST discuss mailing list][cite]
A common pattern for Web APIs is to use filename extensions on URLs to provide an endpoint for a given media type. For example, 'http://example.com/api/users.json' to serve a JSON representation.
Adding format-suffix patterns to each individual entry in the URLconf for your API is error-prone and non-DRY, so REST framework provides a shortcut to adding these patterns to your URLConf.
## format_suffix_patterns
**Signature**: format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None)
Returns a URL pattern list which includes format suffix patterns appended to each of the URL patterns provided.
Arguments:
* **urlpatterns**: Required. A URL pattern list.
* **suffix_required**: Optional. A boolean indicating if suffixes in the URLs should be optional or mandatory. Defaults to `False`, meaning that suffixes are optional by default.
* **allowed**: Optional. A list or tuple of valid format suffixes. If not provided, a wildcard format suffix pattern will be used.
Example:
from rest_framework.urlpatterns import format_suffix_patterns
urlpatterns = patterns('blog.views',
url(r'^/$', 'api_root'),
url(r'^comment/$', 'comment_root'),
url(r'^comment/(?P<pk>[0-9]+)/$', 'comment_instance')
)
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
When using `format_suffix_patterns`, you must make sure to add the `'format'` keyword argument to the corresponding views. For example.
@api_view(('GET',))
def api_root(request, format=None):
# do stuff...
The name of the kwarg used may be modified by using the `FORMAT_SUFFIX_KWARG` setting.
Also note that `format_suffix_patterns` does not support descending into `include` URL patterns.
---
## 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.
It is actually a misconception. For example, take the following quote from Roy Fielding discussing the relative merits of query parameter media-type indicators vs. file extension media-type indicators:
&ldquo;That's why I always prefer extensions. Neither choice has anything to do with REST.&rdquo; &mdash; Roy Fielding, [REST discuss mailing list][cite2]
The quote does not mention Accept headers, but it does make it clear that format suffixes should be considered an acceptable pattern.
[cite]: http://tech.groups.yahoo.com/group/rest-discuss/message/5857
[cite2]: http://tech.groups.yahoo.com/group/rest-discuss/message/14844
\ No newline at end of file
<a class="github" href="mixins.py"></a>
<a class="github" href="generics.py"></a>
# Generic views
> Django’s generic views... were developed as a shortcut for common usage patterns... They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to repeat yourself.
>
> &mdash; [Django Documentation][cite]
One of the key benefits of class based views is the way they allow you to compose bits of reusable behaviour. 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.
If the generic views don't suit the needs of your API, you can drop down to using the regular `APIView` class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.
## Examples
Typically when using the generic views, you'll override the view, and set several class attributes.
class UserList(generics.ListCreateAPIView):
model = User
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.
class UserList(generics.ListCreateAPIView):
model = User
serializer_class = UserSerializer
permission_classes = (IsAdminUser,)
def get_paginate_by(self, queryset):
"""
Use smaller pagination for HTML representations.
"""
page_size_param = self.request.QUERY_PARAMS.get('page_size')
if page_size_param:
return int(page_size_param)
return 100
For very simple cases you might want to pass through any class attributes using the `.as_view()` method. For example, your URLconf might include something the following entry.
url(r'^/users/', ListCreateAPIView.as_view(model=User) name='user-list')
---
# API Reference
The following classes are the concrete generic views. If you're using generic views this is normally the level you'll be working at unless you need heavily customized behavior.
## CreateAPIView
Used for **create-only** endpoints.
Provides `post` method handlers.
Extends: [GenericAPIView], [CreateModelMixin]
## ListAPIView
Used for **read-only** endpoints to represent a **collection of model instances**.
Provides a `get` method handler.
Extends: [MultipleObjectAPIView], [ListModelMixin]
## RetrieveAPIView
Used for **read-only** endpoints to represent a **single model instance**.
Provides a `get` method handler.
Extends: [SingleObjectAPIView], [RetrieveModelMixin]
## DestroyAPIView
Used for **delete-only** endpoints for a **single model instance**.
Provides a `delete` method handler.
Extends: [SingleObjectAPIView], [DestroyModelMixin]
## UpdateAPIView
Used for **update-only** endpoints for a **single model instance**.
Provides a `put` method handler.
Extends: [SingleObjectAPIView], [UpdateModelMixin]
## ListCreateAPIView
Used for **read-write** endpoints to represent a **collection of model instances**.
Provides `get` and `post` method handlers.
Extends: [MultipleObjectAPIView], [ListModelMixin], [CreateModelMixin]
## RetrieveDestroyAPIView
Used for **read or delete** endpoints to represent a **single model instance**.
Provides `get` and `delete` method handlers.
Extends: [SingleObjectAPIView], [RetrieveModelMixin], [DestroyModelMixin]
## RetrieveUpdateDestroyAPIView
Used for **read-write-delete** endpoints to represent a **single model instance**.
Provides `get`, `put` and `delete` method handlers.
Extends: [SingleObjectAPIView], [RetrieveModelMixin], [UpdateModelMixin], [DestroyModelMixin]
---
# Base views
Each of the generic views provided is built by combining one of the base views below, with one or more mixin classes.
## GenericAPIView
Extends REST framework's `APIView` class, adding support for serialization of model instances and model querysets.
## MultipleObjectAPIView
Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [MultipleObjectMixin].
**See also:** ccbv.co.uk documentation for [MultipleObjectMixin][multiple-object-mixin-classy].
## SingleObjectAPIView
Provides a base view for acting on a single object, by combining REST framework's `APIView`, and Django's [SingleObjectMixin].
**See also:** ccbv.co.uk documentation for [SingleObjectMixin][single-object-mixin-classy].
---
# Mixins
The mixin classes provide the actions that are used to provide the basic view behaviour. Note that the mixin classes provide action methods rather than defining the handler methods such as `.get()` and `.post()` directly. This allows for more flexible composition of behaviour.
## ListModelMixin
Provides a `.list(request, *args, **kwargs)` method, that implements listing a queryset.
Should be mixed in with [MultipleObjectAPIView].
## CreateModelMixin
Provides a `.create(request, *args, **kwargs)` method, that implements creating and saving a new model instance.
Should be mixed in with any [GenericAPIView].
## RetrieveModelMixin
Provides a `.retrieve(request, *args, **kwargs)` method, that implements returning an existing model instance in a response.
Should be mixed in with [SingleObjectAPIView].
## UpdateModelMixin
Provides a `.update(request, *args, **kwargs)` method, that implements updating and saving an existing model instance.
Should be mixed in with [SingleObjectAPIView].
## DestroyModelMixin
Provides a `.destroy(request, *args, **kwargs)` method, that implements deletion of an existing model instance.
Should be mixed in with [SingleObjectAPIView].
[cite]: https://docs.djangoproject.com/en/dev/ref/class-based-views/#base-vs-generic-views
[MultipleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-multiple-object/
[SingleObjectMixin]: https://docs.djangoproject.com/en/dev/ref/class-based-views/mixins-single-object/
[multiple-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.list/MultipleObjectMixin/
[single-object-mixin-classy]: http://ccbv.co.uk/projects/Django/1.4/django.views.generic.detail/SingleObjectMixin/
[GenericAPIView]: #genericapiview
[SingleObjectAPIView]: #singleobjectapiview
[MultipleObjectAPIView]: #multipleobjectapiview
[ListModelMixin]: #listmodelmixin
[CreateModelMixin]: #createmodelmixin
[RetrieveModelMixin]: #retrievemodelmixin
[UpdateModelMixin]: #updatemodelmixin
[DestroyModelMixin]: #destroymodelmixin
\ No newline at end of file
<a class="github" href="pagination.py"></a>
# Pagination
> Django provides a few classes that help you manage paginated data – that is, data that’s split across several pages, with “Previous/Next” links.
>
> &mdash; [Django documentation][cite]
REST framework includes a `PaginationSerializer` class that makes it easy to return paginated data in a way that can then be rendered to arbitrary media types.
## Paginating basic data
Let's start by taking a look at an example from the Django documentation.
from django.core.paginator import Paginator
objects = ['john', 'paul', 'george', 'ringo']
paginator = Paginator(objects, 2)
page = paginator.page(1)
page.object_list
# ['john', 'paul']
At this point we've got a page object. If we wanted to return this page object as a JSON response, we'd need to provide the client with context such as next and previous links, so that it would be able to page through the remaining results.
from rest_framework.pagination import PaginationSerializer
serializer = PaginationSerializer(instance=page)
serializer.data
# {'count': 4, 'next': '?page=2', 'previous': None, 'results': [u'john', u'paul']}
The `context` argument of the `PaginationSerializer` class may optionally include the request. If the request is included in the context then the next and previous links returned by the serializer will use absolute URLs instead of relative URLs.
request = RequestFactory().get('/foobar')
serializer = PaginationSerializer(instance=page, context={'request': request})
serializer.data
# {'count': 4, 'next': 'http://testserver/foobar?page=2', 'previous': None, 'results': [u'john', u'paul']}
We could now return that data in a `Response` object, and it would be rendered into the correct media type.
## Paginating QuerySets
Our first example worked because we were using primative objects. If we wanted to paginate a queryset or other complex data, we'd need to specify a serializer to use to serialize the result set itself with.
We can do this using the `object_serializer_class` attribute on the inner `Meta` class of the pagination serializer. For example.
class UserSerializer(serializers.ModelSerializer):
"""
Serializes user querysets.
"""
class Meta:
model = User
fields = ('username', 'email')
class PaginatedUserSerializer(pagination.PaginationSerializer):
"""
Serializes page objects of user querysets.
"""
class Meta:
object_serializer_class = UserSerializer
We could now use our pagination serializer in a view like this.
@api_view('GET')
def user_list(request):
queryset = User.objects.all()
paginator = Paginator(queryset, 20)
page = request.QUERY_PARAMS.get('page')
try:
users = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
users = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
users = paginator.page(paginator.num_pages)
serializer_context = {'request': request}
serializer = PaginatedUserSerializer(instance=users,
context=serializer_context)
return Response(serializer.data)
## Pagination in the generic views
The generic class based views `ListAPIView` and `ListCreateAPIView` provide pagination of the returned querysets by default. You can customise this behaviour by altering the pagination style, by modifying the default number of results, or by turning pagination off completely.
The default pagination style may be set globally, using the `PAGINATION_SERIALIZER` and `PAGINATE_BY` settings. For example.
REST_FRAMEWORK = {
'PAGINATION_SERIALIZER': (
'example_app.pagination.CustomPaginationSerializer',
),
'PAGINATE_BY': 10
}
You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view.
class PaginatedListView(ListAPIView):
model = ExampleModel
pagination_serializer_class = CustomPaginationSerializer
paginate_by = 10
For more complex requirements such as serialization that differs depending on the requested media type you can override the `.get_paginate_by()` and `.get_pagination_serializer_class()` methods.
---
# Custom pagination serializers
To create a custom pagination serializer class you should override `pagination.BasePaginationSerializer` and set the fields that you want the serializer to return.
You can also override the name used for the object list field, by setting the `results_field` attribute, which defaults to `'results'`.
## Example
For example, to nest a pair of links labelled 'prev' and 'next', and set the name for the results field to 'objects', you might use something like this.
class LinksSerializer(serializers.Serializer):
next = pagination.NextURLField(source='*')
prev = pagination.PreviousURLField(source='*')
class CustomPaginationSerializer(pagination.BasePaginationSerializer):
links = LinksSerializer(source='*') # Takes the page object as the source
total_results = serializers.Field(source='paginator.count')
results_field = 'objects'
[cite]: https://docs.djangoproject.com/en/dev/topics/pagination/
<a class="github" href="parsers.py"></a>
# Parsers
> Machine interacting web services tend to use more
structured formats for sending data than form-encoded, since they're
sending more complex data than simple forms
>
> &mdash; Malcom Tredinnick, [Django developers group][cite]
REST framework includes a number of built in Parser classes, that allow you to accept requests with various media types. There is also support for defining your own custom parsers, which gives you the flexibility to design the media types that your API accepts.
## How the parser is determined
The set of valid parsers for a view is always defined as a list of classes. When either `request.DATA` or `request.FILES` is accessed, REST framework will examine the `Content-Type` header on the incoming request, and determine which parser to use to parse the request content.
## Setting the parsers
The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSES` setting. For example, the following settings would allow requests with `YAML` content.
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.YAMLParser',
)
}
You can also set the renderers used for an individual view, using the `APIView` class based views.
class ExampleView(APIView):
"""
A view that can accept POST requests with YAML content.
"""
parser_classes = (YAMLParser,)
def post(self, request, format=None):
return Response({'received data': request.DATA})
Or, if you're using the `@api_view` decorator with function based views.
@api_view(['POST'])
@parser_classes((YAMLParser,))
def example_view(request, format=None):
"""
A view that can accept POST requests with YAML content.
"""
return Response({'received data': request.DATA})
---
# API Reference
## JSONParser
Parses `JSON` request content.
**.media_type**: `application/json`
## YAMLParser
Parses `YAML` request content.
**.media_type**: `application/yaml`
## XMLParser
Parses REST framework's default style of `XML` request content.
Note that the `XML` markup language is typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
**.media_type**: `application/xml`
## FormParser
Parses HTML form content. `request.DATA` will be populated with a `QueryDict` of data, `request.FILES` will be populated with an empty `QueryDict` of data.
You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
**.media_type**: `application/x-www-form-urlencoded`
## MultiPartParser
Parses multipart HTML form content, which supports file uploads. Both `request.DATA` and `request.FILES` will be populated with a `QueryDict`.
You will typically want to use both `FormParser` and `MultiPartParser` together in order to fully support HTML form data.
**.media_type**: `multipart/form-data`
---
# Custom parsers
To implement a custom parser, you should override `BaseParser`, set the `.media_type` property, and implement the `.parse(self, stream, media_type, parser_context)` method.
The method should return the data that will be used to populate the `request.DATA` property.
The arguments passed to `.parse()` are:
### stream
A stream-like object representing the body of the request.
### media_type
Optional. If provided, this is the media type of the incoming request content.
Depending on the request's `Content-Type:` header, this may be more specific than the renderer's `media_type` attribute, and may include media type parameters. For example `"text/plain; charset=utf-8"`.
### parser_context
Optional. If supplied, this argument will be a dictionary containing any additional context that may be required to parse the request content.
By default this will include the following keys: `view`, `request`, `args`, `kwargs`.
## Example
The following is an example plaintext parser that will populate the `request.DATA` property with a string representing the body of the request.
class PlainTextParser(BaseParser):
"""
Plain text parser.
"""
media_type = 'text/plain'
def parse(self, stream, media_type=None, parser_context=None):
"""
Simply return a string representing the body of the request.
"""
return stream.read()
## Uploading file content
If your custom parser needs to support file uploads, you may return a `DataAndFiles` object from the `.parse()` method. `DataAndFiles` should be instantiated with two arguments. The first argument will be used to populate the `request.DATA` property, and the second argument will be used to populate the `request.FILES` property.
For example:
class SimpleFileUploadParser(BaseParser):
"""
A naive raw file upload parser.
"""
def parse(self, stream, media_type=None, parser_context=None):
content = stream.read()
name = 'example.dat'
content_type = 'application/octet-stream'
size = len(content)
charset = 'utf-8'
# Write a temporary file based on the request content
temp = tempfile.NamedTemporaryFile(delete=False)
temp.write(content)
uploaded = UploadedFile(temp, name, content_type, size, charset)
# Return the uploaded file
data = {}
files = {name: uploaded}
return DataAndFiles(data, files)
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
<a class="github" href="permissions.py"></a>
# Permissions
> Authentication or identification by itself is not usually sufficient to gain access to information or code. For that, the entity requesting access must have authorization.
>
> &mdash; [Apple Developer Documentation][cite]
Together with [authentication] and [throttling], permissions determine whether a request should be granted or denied access.
Permission checks are always run at the very start of the view, before any other code is allowed to proceed. Permission checks will typically use the authentication information in the `request.user` and `request.auth` properties to determine if the incoming request should be permitted.
## How permissions are determined
Permissions in REST framework are always defined as a list of permission classes.
Before running the main body of the view each permission in the list is checked.
If any permission check fails an `exceptions.PermissionDenied` exception will be raised, and the main body of the view will not run.
## Object level permissions
REST framework permissions also support object-level permissioning. Object level permissions are used to determine if a user should be allowed to act on a particular object, which will typically be a model instance.
Object level permissions are run by REST framework's generic views when `.get_object()` is called. As with view level permissions, an `exceptions.PermissionDenied` exception will be raised if the user is not allowed to act on the given object.
## Setting the permission policy
The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
If not specified, this setting defaults to allowing unrestricted access:
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
)
You can also set the authentication policy on a per-view basis, using the `APIView` class based views.
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
Or, if you're using the `@api_view` decorator with function based views.
@api_view('GET')
@permission_classes(IsAuthenticated)
def example_view(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
---
# API Reference
## AllowAny
The `AllowAny` permission class will allow unrestricted access, **regardless of if the request was authenticated or unauthenticated**.
This permission is not strictly required, since you can achieve the same result by using an empty list or tuple for the permissions setting, but you may find it useful to specify this class because it makes the intention explicit.
## IsAuthenticated
The `IsAuthenticated` permission class will deny permission to any unauthenticated user, and allow permission otherwise.
This permission is suitable if you want your API to only be accessible to registered users.
## IsAdminUser
The `IsAdminUser` permission class will deny permission to any user, unless `user.is_staff` is `True` in which case permission will be allowed.
This permission is suitable is you want your API to only be accessible to a subset of trusted administrators.
## IsAuthenticatedOrReadOnly
The `IsAuthenticatedOrReadOnly` will allow authenticated users to perform any request. Requests for unauthorised users will only be permitted if the request method is one of the "safe" methods; `GET`, `HEAD` or `OPTIONS`.
This permission is suitable if you want to your API to allow read permissions to anonymous users, and only allow write permissions to authenticated users.
## DjangoModelPermissions
This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user 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.
* `DELETE` requests require the user to have the `delete` permission on the model.
The default behaviour can also be overridden to support custom model permissions. For example, you might want to include a `view` model permission for `GET` requests.
To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details.
The `DjangoModelPermissions` class also supports object-level permissions. Third-party authorization backends such as [django-guardian][guardian] that provide object-level permissions should work just fine with `DjangoModelPermissions` without any custom configuration required.
---
# Custom permissions
To implement a custom permission, override `BasePermission` and implement the `.has_permission(self, request, view, obj=None)` method.
The method should return `True` if the request should be granted access, and `False` otherwise.
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
[authentication]: authentication.md
[throttling]: throttling.md
[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions
[guardian]: https://github.com/lukaszb/django-guardian
<a class="github" href="request.py"></a>
# Requests
> If you're doing REST-based web service stuff ... you should ignore request.POST.
>
> &mdash; Malcom Tredinnick, [Django developers group][cite]
REST framework's `Request` class extends the standard `HttpRequest`, adding support for REST framework's flexible request parsing and request authentication.
---
# Request parsing
REST framework's Request objects provide flexible request parsing that allows you to treat requests with JSON data or other media types in the same way that you would normally deal with form data.
## .DATA
`request.DATA` returns the parsed content of the request body. This is similar to the standard `request.POST` attribute except that:
* It supports parsing the content of HTTP methods other than `POST`, meaning that you can access the content of `PUT` and `PATCH` requests.
* It supports REST framework's flexible request parsing, rather than just supporting form data. For example you can handle incoming JSON data in the same way that you handle incoming form data.
For more details see the [parsers documentation].
## .FILES
`request.FILES` returns any uploaded files that may be present in the content of the request body. This is the same as the standard `HttpRequest` behavior, except that the same flexible request parsing is used for `request.DATA`.
For more details see the [parsers documentation].
## .QUERY_PARAMS
`request.QUERY_PARAMS` is a more correctly named synonym for `request.GET`.
For clarity inside your code, we recommend using `request.QUERY_PARAMS` instead of the usual `request.GET`, as *any* HTTP method type may include query parameters.
## .parsers
The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Parser` instances, based on the `parser_classes` set on the view or based on the `DEFAULT_PARSER_CLASSES` setting.
You won't typically need to access this property.
---
**Note:** If a client sends malformed content, then accessing `request.DATA` or `request.FILES` may raise a `ParseError`. By default REST framework's `APIView` class or `@api_view` decorator will catch the error and return a `400 Bad Request` response.
If a client sends a request with a content-type that cannot be parsed then a `UnsupportedMediaType` exception will be raised, which by default will be caught and return a `415 Unsupported Media Type` response.
---
# Authentication
REST framework provides flexible, per-request authentication, that gives you the ability to:
* Use different authentication policies for different parts of your API.
* Support the use of multiple authentication policies.
* Provide both user and token information associated with the incoming request.
## .user
`request.user` typically returns an instance of `django.contrib.auth.models.User`, although the behavior depends on the authentication policy being used.
If the request is unauthenticated the default value of `request.user` is an instance of `django.contrib.auth.models.AnonymousUser`.
For more details see the [authentication documentation].
## .auth
`request.auth` returns any additional authentication context. The exact behavior of `request.auth` depends on the authentication policy being used, but it may typically be an instance of the token that the request was authenticated against.
If the request is unauthenticated, or if no additional context is present, the default value of `request.auth` is `None`.
For more details see the [authentication documentation].
## .authenticators
The `APIView` class or `@api_view` decorator will ensure that this property is automatically set to a list of `Authentication` instances, based on the `authentication_classes` set on the view or based on the `DEFAULT_AUTHENTICATORS` setting.
You won't typically need to access this property.
---
# Browser enhancements
REST framework supports a few browser enhancements such as browser-based `PUT` and `DELETE` forms.
## .method
`request.method` returns the **uppercased** string representation of the request's HTTP method.
Browser-based `PUT` and `DELETE` forms are transparently supported.
For more information see the [browser enhancements documentation].
## .content_type
`request.content_type`, returns a string object representing the media type of the HTTP request's body, or an empty string if no media type was provided.
You won't typically need to directly access the request's content type, as you'll normally rely on REST framework's default request parsing behavior.
If you do need to access the content type of the request you should use the `.content_type` property in preference to using `request.META.get('HTTP_CONTENT_TYPE')`, as it provides transparent support for browser-based non-form content.
For more information see the [browser enhancements documentation].
## .stream
`request.stream` returns a stream representing the content of the request body.
You won't typically need to directly access the request's content, as you'll normally rely on REST framework's default request parsing behavior.
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
As REST framework's `Request` extends Django's `HttpRequest`, all the other standard attributes and methods are also available. For example the `request.META` dictionary is available as normal.
Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition.
[cite]: https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion
[parsers documentation]: parsers.md
[authentication documentation]: authentication.md
[browser enhancements documentation]: ../topics/browser-enhancements.md
<a class="github" href="response.py"></a>
# Responses
> Unlike basic HttpResponse objects, TemplateResponse objects retain the details of the context that was provided by the view to compute the response. The final output of the response is not computed until it is needed, later in the response process.
>
> &mdash; [Django documentation][cite]
REST framework supports HTTP content negotiation by providing a `Response` class which allows you to return content that can be rendered into multiple content types, depending on the client request.
The `Response` class subclasses Django's `SimpleTemplateResponse`. `Response` objects are initialised with data, which should consist of native python primatives. REST framework then uses standard HTTP content negotiation to determine how it should render the final response content.
There's no requirement for you to use the `Response` class, you can also return regular `HttpResponse` objects from your views if you want, but it provides a nicer interface for returning Web API responses.
Unless you want to heavily customize REST framework for some reason, you should always use an `APIView` class or `@api_view` function for views that return `Response` objects. Doing so ensures that the view can perform content negotiation and select the appropriate renderer for the response, before it is returned from the view.
---
# Creating responses
## Response()
**Signature:** `Response(data, status=None, template_name=None, headers=None)`
Unlike regular `HttpResponse` objects, you do not instantiate `Response` objects with rendered content. Instead you pass in unrendered data, which may consist of any python primatives.
The renderers used by the `Response` class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primative datatypes before creating the `Response` object.
You can use REST framework's `Serializer` classes to perform this data serialization, or use your own custom serialization.
Arguments:
* `data`: The serialized data for the response.
* `status`: A status code for the response. Defaults to 200. See also [status codes][statuscodes].
* `template_name`: A template name to use if `HTMLRenderer` is selected.
* `headers`: A dictionary of HTTP headers to use in the response.
---
# Attributes
## .data
The unrendered content of a `Request` object.
## .status_code
The numeric status code of the HTTP response.
## .content
The rendered content of the response. The `.render()` method must have been called before `.content` can be accessed.
## .template_name
The `template_name`, if supplied. Only required if `HTMLRenderer` or some other custom template renderer is the accepted renderer for the reponse.
## .accepted_renderer
The renderer instance that will be used to render the response.
Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
## .accepted_media_type
The media type that was selected by the content negotiation stage.
Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
## .renderer_context
A dictionary of additional context information that will be passed to the renderer's `.render()` method.
Set automatically by the `APIView` or `@api_view` immediately before the response is returned from the view.
---
# Standard HttpResponse attributes
The `Response` class extends `SimpleTemplateResponse`, and all the usual attributes and methods are also available on the response. For example you can set headers on the response in the standard way:
response = Response()
response['Cache-Control'] = 'no-cache'
## .render()
**Signature:** `.render()`
As with any other `TemplateResponse`, this method is called to render the serialized data of the response into the final response content. When `.render()` is called, the response content will be set to the result of calling the `.render(data, accepted_media_type, renderer_context)` method on the `accepted_renderer` instance.
You won't typically need to call `.render()` yourself, as it's handled by Django's standard response cycle.
[cite]: https://docs.djangoproject.com/en/dev/ref/template-response/
[statuscodes]: status-codes.md
<a class="github" href="reverse.py"></a>
# Returning URLs
> The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components.
>
> &mdash; Roy Fielding, [Architectural Styles and the Design of Network-based Software Architectures][cite]
As a rule, it's probably better practice to return absolute URIs from your Web APIs, such as `http://example.com/foobar`, rather than returning relative URIs, such as `/foobar`.
The advantages of doing so are:
* It's more explicit.
* It leaves less work for your API clients.
* There's no ambiguity about the meaning of the string when it's found in representations such as JSON that do not have a native URI type.
* It makes it easy to do things like markup HTML representations with hyperlinks.
REST framework provides two utility functions to make it more simple to return absolute URIs from your Web API.
There's no requirement for you to use them, but if you do then the self-describing API will be able to automatically hyperlink it's output for you, which makes browsing the API much easier.
## reverse
**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.
You should **include the request as a keyword argument** to the function, for example:
import datetime
from rest_framework.reverse import reverse
from rest_framework.views import APIView
class APIRootView(APIView):
def get(self, request):
year = datetime.datetime.now().year
data = {
...
'year-summary-url': reverse('year-summary', args=[year], request=request)
}
return Response(data)
## reverse_lazy
**Signature:** `reverse_lazy(viewname, *args, **kwargs)`
Has the same behavior as [`django.core.urlresolvers.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
<a class="github" href="settings.py"></a>
# Settings
> Namespaces are one honking great idea - let's do more of those!
>
> &mdash; [The Zen of Python][cite]
Configuration for REST framework is all namespaced inside a single Django setting, named `REST_FRAMEWORK`.
For example your project's `settings.py` file might include something like this:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.YAMLRenderer',
)
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.YAMLParser',
)
}
## Accessing settings
If you need to access the values of REST framework's API settings in your project,
you should use the `api_settings` object. For example.
from rest_framework.settings import api_settings
print api_settings.DEFAULT_AUTHENTICATION_CLASSES
The `api_settings` object will check for any user-defined settings, and otherwise fallback to the default values. Any setting that uses string import paths to refer to a class will automatically import and return the referenced class, instead of the string literal.
---
# API Reference
## DEFAULT_RENDERER_CLASSES
A list or tuple of renderer classes, that determines the default set of renderers that may be used when returning a `Response` object.
Default:
(
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
'rest_framework.renderers.TemplateHTMLRenderer'
)
## DEFAULT_PARSER_CLASSES
A list or tuple of parser classes, that determines the default set of parsers used when accessing the `request.DATA` property.
Default:
(
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser'
)
## DEFAULT_AUTHENTICATION_CLASSES
A list or tuple of authentication classes, that determines the default set of authenticators used when accessing the `request.user` or `request.auth` properties.
Default:
(
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.UserBasicAuthentication'
)
## DEFAULT_PERMISSION_CLASSES
A list or tuple of permission classes, that determines the default set of permissions checked at the start of a view.
Default:
(
'rest_framework.permissions.AllowAny',
)
## DEFAULT_THROTTLE_CLASSES
A list or tuple of throttle classes, that determines the default set of throttles checked at the start of a view.
Default: `()`
## DEFAULT_MODEL_SERIALIZER_CLASS
**TODO**
Default: `rest_framework.serializers.ModelSerializer`
## DEFAULT_PAGINATION_SERIALIZER_CLASS
**TODO**
Default: `rest_framework.pagination.PaginationSerializer`
## FORMAT_SUFFIX_KWARG
**TODO**
Default: `'format'`
## UNAUTHENTICATED_USER
The class that should be used to initialize `request.user` for unauthenticated requests.
Default: `django.contrib.auth.models.AnonymousUser`
## UNAUTHENTICATED_TOKEN
The class that should be used to initialize `request.auth` for unauthenticated requests.
Default: `None`
## 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.
Default: `'_method'`
## FORM_CONTENT_OVERRIDE
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`.
If either setting is `None` then form content overloading will be disabled.
Default: `'_content'`
## FORM_CONTENTTYPE_OVERRIDE
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.
Default: `'_content_type'`
## URL_ACCEPT_OVERRIDE
The name of a URL parameter that may be used to override the HTTP `Accept` header.
If the value of this setting is `None` then URL accept overloading will be disabled.
Default: `'accept'`
## URL_FORMAT_OVERRIDE
Default: `'format'`
[cite]: http://www.python.org/dev/peps/pep-0020/
<a class="github" href="status.py"></a>
# Status Codes
> 418 I'm a teapot - Any attempt to brew coffee with a teapot should result in the error code "418 I'm a teapot". The resulting entity body MAY be short and stout.
>
> &mdash; [RFC 2324][rfc2324], Hyper Text Coffee Pot Control Protocol
Using bare status codes in your responses isn't recommended. REST framework includes a set of named constants that you can use to make more code more obvious and readable.
from rest_framework import status
def empty_view(self):
content = {'please move along': 'nothing to see here'}
return Response(content, status=status.HTTP_404_NOT_FOUND)
The full set of HTTP status codes included in the `status` module is listed below.
For more information on proper usage of HTTP status codes see [RFC 2616][rfc2616]
and [RFC 6585][rfc6585].
## Informational - 1xx
This class of status code indicates a provisional response. There are no 1xx status codes used in REST framework by default.
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
## Successful - 2xx
This class of status code indicates that the client's request was successfully received, understood, and accepted.
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
## Redirection - 3xx
This class of status code indicates that further action needs to be taken by the user agent in order to fulfill the request.
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
## Client Error - 4xx
The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition.
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
## Server Error - 5xx
Response status codes beginning with the digit "5" indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition.
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_511_NETWORD_AUTHENTICATION_REQUIRED
[rfc2324]: http://www.ietf.org/rfc/rfc2324.txt
[rfc2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
[rfc6585]: http://tools.ietf.org/html/rfc6585
<a class="github" href="throttling.py"></a>
# Throttling
> HTTP/1.1 420 Enhance Your Calm
>
> [Twitter API rate limiting response][cite]
[cite]: https://dev.twitter.com/docs/error-codes-responses
Throttling is similar to [permissions], in that it determines if a request should be authorized. Throttles indicate a temporary state, and are used to control the rate of requests that clients can make to an API.
As with permissions, multiple throttles may be used. Your API might have a restrictive throttle for unauthenticated requests, and a less restrictive throttle for authenticated requests.
Another scenario where you might want to use multiple throttles would be if you need to impose different constraints on different parts of the API, due to some services being particularly resource-intensive.
Multiple throttles can also be used if you want to impose both burst throttling rates, and sustained throttling rates. For example, you might want to limit a user to a maximum of 60 requests per minute, and 1000 requests per day.
Throttles do not necessarily only refer to rate-limiting requests. For example a storage service might also need to throttle against bandwidth, and a paid data service might want to throttle against a certain number of a records being accessed.
## How throttling is determined
As with permissions and authentication, throttling in REST framework is always defined as a list of classes.
Before running the main body of the view each throttle in the list is checked.
If any throttle check fails an `exceptions.Throttled` exception will be raised, and the main body of the view will not run.
## Setting the throttling policy
The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_CLASSES` and `DEFAULT_THROTTLE_RATES` settings. For example.
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttles.AnonThrottle',
'rest_framework.throttles.UserThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
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 basis, using the `APIView` class based views.
class ExampleView(APIView):
throttle_classes = (UserThrottle,)
def get(self, request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
Or, if you're using the `@api_view` decorator with function based views.
@api_view('GET')
@throttle_classes(UserThrottle)
def example_view(request, format=None):
content = {
'status': 'request was permitted'
}
return Response(content)
---
# API Reference
## AnonRateThrottle
The `AnonThrottle` will only ever throttle unauthenticated users. The IP address of the incoming request is used to generate a unique key to throttle against.
The allowed request rate is determined from one of the following (in order of preference).
* The `rate` property on the class, which may be provided by overriding `AnonThrottle` and setting the property.
* The `DEFAULT_THROTTLE_RATES['anon']` setting.
`AnonThrottle` is suitable if you want to restrict the rate of requests from unknown sources.
## UserRateThrottle
The `UserThrottle` will throttle users to a given rate of requests across the API. The user id is used to generate a unique key to throttle against. Unauthenticated requests will fall back to using the IP address of the incoming request to generate a unique key to throttle against.
The allowed request rate is determined from one of the following (in order of preference).
* The `rate` property on the class, which may be provided by overriding `UserThrottle` and setting the property.
* The `DEFAULT_THROTTLE_RATES['user']` setting.
An API may have multiple `UserRateThrottles` in place at the same time. To do so, override `UserRateThrottle` and set a unique "scope" for each class.
For example, multiple user throttle rates could be implemented by using the following classes...
class BurstRateThrottle(UserRateThrottle):
scope = 'burst'
class SustainedRateThrottle(UserRateThrottle):
scope = 'sustained'
...and the following settings.
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'example.throttles.BurstRateThrottle',
'example.throttles.SustainedRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'burst': '60/min',
'sustained': '1000/day'
}
}
`UserThrottle` is suitable if you want simple global rate restrictions per-user.
## ScopedRateThrottle
The `ScopedThrottle` class can be used to restrict access to specific parts of the API. This throttle will only be applied if the view that is being accessed includes a `.throttle_scope` property. The unique throttle key will then be formed by concatenating the "scope" of the request with the unique user id or IP address.
The allowed request rate is determined by the `DEFAULT_THROTTLE_RATES` setting using a key from the request "scope".
For example, given the following views...
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(ApiView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
...and the following settings.
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttles.ScopedRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day.
---
# Custom throttles
To create a custom throttle, override `BaseThrottle` and implement `.allow_request(request, view)`. The method should return `True` if the request should be allowed, and `False` otherwise.
Optionally you may also override the `.wait()` method. If implemented, `.wait()` should return a recommended number of seconds to wait before attempting the next request, or `None`. The `.wait()` method will only be called if `.allow_request()` has previously returned `False`.
[permissions]: permissions.md
<a class="github" href="decorators.py"></a> <a class="github" href="views.py"></a>
# Class Based Views
> Django's class based views are a welcome departure from the old-style views.
>
> &mdash; [Reinout van Rees][cite]
REST framework provides an `APIView` class, which subclasses Django's `View` class.
`APIView` classes are different from regular `View` classes in the following ways:
* Requests passed to the handler methods will be REST framework's `Request` instances, not Django's `HttpRequest` instances.
* Handler methods may return REST framework's `Response`, instead of Django's `HttpResponse`. The view will manage content negotiation and setting the correct renderer on the response.
* Any `APIException` exceptions will be caught and mediated into appropriate responses.
* Incoming requests will be authenticated and appropriate permission and/or throttle checks will be run before dispatching the request to the handler method.
Using the `APIView` class is pretty much the same as using a regular `View` class, as usual, the incoming request is dispatched to an appropriate handler method such as `.get()` or `.post()`. Additionally, a number of attributes may be set on the class that control various aspects of the API policy.
For example:
class ListUsers(APIView):
"""
View to list all users in the system.
* Requires token authentication.
* Only admin users are able to access this view.
"""
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAdminUser,)
def get(self, request, format=None):
"""
Return a list of all users.
"""
usernames = [user.username for user in User.objects.all()]
return Response(usernames)
## API policy attributes
The following attributes control the pluggable aspects of API views.
### .renderer_classes
### .parser_classes
### .authentication_classes
### .throttle_classes
### .permission_classes
### .content_negotiation_class
## API policy instantiation methods
The following methods are used by REST framework to instantiate the various pluggable API policies. You won't typically need to override these methods.
### .get_renderers(self)
### .get_parsers(self)
### .get_authenticators(self)
### .get_throttles(self)
### .get_permissions(self)
### .get_content_negotiator(self)
## API policy implementation methods
The following methods are called before dispatching to the handler method.
### .check_permissions(...)
### .check_throttles(...)
### .perform_content_negotiation(...)
## Dispatch methods
The following methods are called directly by the view's `.dispatch()` method.
These perform any actions that need to occur before or after calling the handler methods such as `.get()`, `.post()`, `put()` and `.delete()`.
### .initial(self, request, \*args, **kwargs)
Performs any actions that need to occur before the handler method gets called.
This method is used to enforce permissions and throttling, and perform content negotiation.
You won't typically need to override this method.
### .handle_exception(self, exc)
Any exception thrown by the handler method will be passed to this method, which either returns a `Response` instance, or re-raises the exception.
The default implementation handles any subclass of `rest_framework.exceptions.APIException`, as well as Django's `Http404` and `PermissionDenied` exceptions, and returns an appropriate error response.
If you need to customize the error responses your API returns you should subclass this method.
### .initialize_request(self, request, \*args, **kwargs)
Ensures that the request object that is passed to the handler method is an instance of `Request`, rather than the usual Django `HttpRequest`.
You won't typically need to override this method.
### .finalize_response(self, request, response, \*args, **kwargs)
Ensures that any `Response` object returned from the handler method will be rendered into the correct content type, as determined by the content negotation.
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.
>
> &mdash; [Nick Coghlan][cite2]
REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of `Request` (rather than the usual Django `HttpRequest`) and allows them to return a `Response` (instead of a Django `HttpResponse`), and allow you to configure how the request is processed.
## @api_view()
**Signature:** `@api_view(http_method_names)`
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:
from rest_framework.decorators import api_view
@api_view(['GET'])
def hello_world(request):
return Response({"message": "Hello, world!"})
This view will use the default renderers, parsers, authentication classes etc specified in the [settings](settings).
## 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:
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle
class OncePerDayUserThrottle(UserRateThrottle):
rate = '1/day'
@api_view(['GET'])
@throttle_classes([OncePerDayUserThrottle])
def view(request):
return Response({"message": "Hello for today! See you tomorrow!"})
These decorators correspond to the attributes set on `APIView` subclasses, described above.
The available decorators are:
* `@renderer_classes(...)`
* `@parser_classes(...)`
* `@authentication_classes(...)`
* `@throttle_classes(...)`
* `@permission_classes(...)`
Each of these decorators takes a single argument which must be a list or tuple of classes.
[cite]: http://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html
[cite2]: http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html
[settings]: api-guide/settings.md
[throttling]: api-guide/throttling.md
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
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