aboutsummaryrefslogtreecommitdiffstats
path: root/docs/api-guide
diff options
context:
space:
mode:
Diffstat (limited to 'docs/api-guide')
-rwxr-xr-xdocs/api-guide/authentication.md16
-rw-r--r--docs/api-guide/content-negotiation.md6
-rw-r--r--docs/api-guide/exceptions.md47
-rw-r--r--docs/api-guide/fields.md13
-rw-r--r--docs/api-guide/filtering.md14
-rwxr-xr-xdocs/api-guide/generic-views.md16
-rw-r--r--docs/api-guide/pagination.md15
-rw-r--r--docs/api-guide/parsers.md6
-rw-r--r--docs/api-guide/permissions.md21
-rw-r--r--docs/api-guide/relations.md15
-rw-r--r--docs/api-guide/renderers.md34
-rw-r--r--docs/api-guide/requests.md2
-rw-r--r--docs/api-guide/reverse.md4
-rw-r--r--docs/api-guide/routers.md7
-rw-r--r--docs/api-guide/serializers.md28
-rw-r--r--docs/api-guide/settings.md79
-rw-r--r--docs/api-guide/status-codes.md1
-rw-r--r--docs/api-guide/testing.md23
-rw-r--r--docs/api-guide/throttling.md11
-rw-r--r--docs/api-guide/viewsets.md13
20 files changed, 324 insertions, 47 deletions
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index b1ab4622..7caeac1e 100755
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -46,6 +46,11 @@ The default authentication schemes may be set globally, using the `DEFAULT_AUTHE
You can also set the authentication scheme on a per-view or per-viewset basis,
using the `APIView` class based views.
+ from rest_framework.authentication import SessionAuthentication, BasicAuthentication
+ from rest_framework.permissions import IsAuthenticated
+ from rest_framework.response import Response
+ from rest_framework.views import APIView
+
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
@@ -157,11 +162,16 @@ The `curl` command line tool may be useful for testing token authenticated APIs.
If you want every user to have an automatically generated Token, you can simply catch the User's `post_save` signal.
+ from django.dispatch import receiver
+ from rest_framework.authtoken.models import Token
+
@receiver(post_save, sender=User)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
+Note that you'll want to ensure you place this code snippet in an installed `models.py` module, or some other location that will be imported by Django on startup.
+
If you've already created some users, you can generate tokens for all existing users like this:
from django.contrib.auth.models import User
@@ -336,6 +346,10 @@ If the `.authenticate_header()` method is not overridden, the authentication sch
The following example will authenticate any incoming request as the user given by the username in a custom request header named 'X_USERNAME'.
+ from django.contrib.auth.models import User
+ from rest_framework import authentication
+ from rest_framework import exceptions
+
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.META.get('X_USERNAME')
@@ -390,4 +404,4 @@ The [Django OAuth2 Consumer][doac] library from [Rediker Software][rediker] is a
[oauthlib]: https://github.com/idan/oauthlib
[doac]: https://github.com/Rediker-Software/doac
[rediker]: https://github.com/Rediker-Software
-[doac-rest-framework]: https://github.com/Rediker-Software/doac/blob/master/docs/markdown/integrations.md#
+[doac-rest-framework]: https://github.com/Rediker-Software/doac/blob/master/docs/integrations.md#
diff --git a/docs/api-guide/content-negotiation.md b/docs/api-guide/content-negotiation.md
index 2a774278..94dd59ca 100644
--- a/docs/api-guide/content-negotiation.md
+++ b/docs/api-guide/content-negotiation.md
@@ -54,6 +54,8 @@ The `select_renderer()` method should return a two-tuple of (renderer instance,
The following is a custom content negotiation class which ignores the client
request when selecting the appropriate parser or renderer.
+ from rest_framework.negotiation import BaseContentNegotiation
+
class IgnoreClientContentNegotiation(BaseContentNegotiation):
def select_parser(self, request, parsers):
"""
@@ -77,6 +79,10 @@ The default content negotiation class may be set globally, using the `DEFAULT_CO
You can also set the content negotiation used for an individual view, or viewset, using the `APIView` class based views.
+ from myapp.negotiation import IgnoreClientContentNegotiation
+ from rest_framework.response import Response
+ from rest_framework.views import APIView
+
class NoNegotiationView(APIView):
"""
An example view that does not perform content negotiation.
diff --git a/docs/api-guide/exceptions.md b/docs/api-guide/exceptions.md
index 8b3e50f1..0c48783a 100644
--- a/docs/api-guide/exceptions.md
+++ b/docs/api-guide/exceptions.md
@@ -28,11 +28,54 @@ For example, the following request:
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-Type: application/json
Content-Length: 42
-
+
{"detail": "Method 'DELETE' not allowed."}
+## Custom exception handling
+
+You can implement custom exception handling by creating a handler function that converts exceptions raised in your API views into response objects. This allows you to control the style of error responses used by your API.
+
+The function must take a single argument, which is the exception to be handled, and should either return a `Response` object, or return `None` if the exception cannot be handled. If the handler returns `None` then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.
+
+For example, you might want to ensure that all error responses include the HTTP status code in the body of the response, like so:
+
+ HTTP/1.1 405 Method Not Allowed
+ Content-Type: application/json
+ Content-Length: 62
+
+ {"status_code": 405, "detail": "Method 'DELETE' not allowed."}
+
+In order to alter the style of the response, you could write the following custom exception handler:
+
+ from rest_framework.views import exception_handler
+
+ def custom_exception_handler(exc):
+ # Call REST framework's default exception handler first,
+ # to get the standard error response.
+ response = exception_handler(exc)
+
+ # Now add the HTTP status code to the response.
+ if response is not None:
+ response.data['status_code'] = response.status_code
+
+ return response
+
+The exception handler must also be configured in your settings, using the `EXCEPTION_HANDLER` setting key. For example:
+
+ REST_FRAMEWORK = {
+ 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
+ }
+
+If not specified, the `'EXCEPTION_HANDLER'` setting defaults to the standard exception handler provided by REST framework:
+
+ REST_FRAMEWORK = {
+ 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
+ }
+
+Note that the exception handler will only be called for responses generated by raised exceptions. It will not be used for any responses returned directly by the view, such as the `HTTP_400_BAD_REQUEST` responses that are returned by the generic views when serializer validation fails.
+
---
# API Reference
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index d69730c9..962c49e2 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -78,6 +78,9 @@ A generic, **read-only** field. You can use this field for any attribute that d
For example, using the following model.
+ from django.db import models
+ from django.utils.timezone import now
+
class Account(models.Model):
owner = models.ForeignKey('auth.user')
name = models.CharField(max_length=100)
@@ -85,13 +88,14 @@ For example, using the following model.
payment_expiry = models.DateTimeField()
def has_expired(self):
- now = datetime.datetime.now()
- return now > self.payment_expiry
+ return now() > self.payment_expiry
A serializer definition that looked like this:
+ from rest_framework import serializers
+
class AccountSerializer(serializers.HyperlinkedModelSerializer):
- expired = Field(source='has_expired')
+ expired = serializers.Field(source='has_expired')
class Meta:
fields = ('url', 'owner', 'name', 'expired')
@@ -125,12 +129,11 @@ The `ModelField` class is generally intended for internal use, but can be used b
This is a read-only field. It gets its value by calling a method on the serializer class it is attached to. It can be used to add any sort of data to the serialized representation of your object. The field's constructor accepts a single argument, which is the name of the method on the serializer to be called. The method should accept a single argument (in addition to `self`), which is the object being serialized. It should return whatever you want to be included in the serialized representation of the object. For example:
- from rest_framework import serializers
from django.contrib.auth.models import User
from django.utils.timezone import now
+ from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
-
days_since_joined = serializers.SerializerMethodField('get_days_since_joined')
class Meta:
diff --git a/docs/api-guide/filtering.md b/docs/api-guide/filtering.md
index 05c997a3..649462da 100644
--- a/docs/api-guide/filtering.md
+++ b/docs/api-guide/filtering.md
@@ -20,6 +20,10 @@ You can do so by filtering based on the value of `request.user`.
For example:
+ from myapp.models import Purchase
+ from myapp.serializers import PurchaseSerializer
+ from rest_framework import generics
+
class PurchaseList(generics.ListAPIView)
serializer_class = PurchaseSerializer
@@ -90,6 +94,11 @@ The default filter backends may be set globally, using the `DEFAULT_FILTER_BACKE
You can also set the filter backends on a per-view, or per-viewset basis,
using the `GenericAPIView` class based views.
+ from django.contrib.auth.models import User
+ from myapp.serializers import UserSerializer
+ from rest_framework import filters
+ from rest_framework import generics
+
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer = UserSerializer
@@ -150,6 +159,11 @@ This will automatically create a `FilterSet` class for the given fields, and wil
For more advanced filtering requirements you can specify a `FilterSet` class that should be used by the view. For example:
+ import django_filters
+ from myapp.models import Product
+ from myapp.serializers import ProductSerializer
+ from rest_framework import generics
+
class ProductFilter(django_filters.FilterSet):
min_price = django_filters.NumberFilter(lookup_type='gte')
max_price = django_filters.NumberFilter(lookup_type='lte')
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index 32a4feef..7185b6b6 100755
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -17,6 +17,11 @@ If the generic views don't suit the needs of your API, you can drop down to usin
Typically when using the generic views, you'll override the view, and set several class attributes.
+ from django.contrib.auth.models import User
+ from myapp.serializers import UserSerializer
+ from rest_framework import generics
+ from rest_framework.permissions import IsAdminUser
+
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
@@ -68,7 +73,7 @@ The following attributes control the basic view behavior.
**Pagination**:
-The following attibutes are used to control pagination when used with list views.
+The following attributes are used to control pagination when used with list views.
* `paginate_by` - The size of pages to use with paginated data. If set to `None` then pagination is turned off. If unset this uses the same value as the `PAGINATE_BY` setting, which defaults to `None`.
* `paginate_by_param` - The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If unset this uses the same value as the `PAGINATE_BY_PARAM` setting, which defaults to `None`.
@@ -108,7 +113,12 @@ For example:
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field]
- return get_object_or_404(queryset, **filter)
+
+ obj = get_object_or_404(queryset, **filter)
+ self.check_object_permissions(self.request, obj)
+ return obj
+
+Note that if your API doesn't include any object level permissions, you may optionally exclude the ``self.check_object_permissions, and simply return the object from the `get_object_or_404` lookup.
#### `get_serializer_class(self)`
@@ -125,7 +135,7 @@ For example:
#### `get_paginate_by(self)`
-Returns the page size to use with pagination. By default this uses the `paginate_by` attribute, and may be overridden by the cient if the `paginate_by_param` attribute is set.
+Returns the page size to use with pagination. By default this uses the `paginate_by` attribute, and may be overridden by the client if the `paginate_by_param` attribute is set.
You may want to override this method to provide more complex behavior such as modifying page sizes based on the media type of the response.
diff --git a/docs/api-guide/pagination.md b/docs/api-guide/pagination.md
index 912ce41b..0829589f 100644
--- a/docs/api-guide/pagination.md
+++ b/docs/api-guide/pagination.md
@@ -13,6 +13,7 @@ REST framework includes a `PaginationSerializer` class that makes it easy to ret
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)
@@ -22,6 +23,7 @@ Let's start by taking a look at an example from the Django documentation.
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']}
@@ -83,11 +85,12 @@ We could now use our pagination serializer in a view like this.
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, by allowing clients to override the page size using a query parameter, or by turning pagination off completely.
-The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY` and `PAGINATE_BY_PARAM` settings. For example.
+The default pagination style may be set globally, using the `DEFAULT_PAGINATION_SERIALIZER_CLASS`, `PAGINATE_BY`, `PAGINATE_BY_PARAM`, and `MAX_PAGINATE_BY` settings. For example.
REST_FRAMEWORK = {
- 'PAGINATE_BY': 10,
- 'PAGINATE_BY_PARAM': 'page_size'
+ 'PAGINATE_BY': 10, # Default to 10
+ 'PAGINATE_BY_PARAM': 'page_size', # Allow client to override, using `?page_size=xxx`.
+ 'MAX_PAGINATE_BY': 100 # Maximum limit allowed when using `?page_size=xxx`.
}
You can also set the pagination style on a per-view basis, using the `ListAPIView` generic class-based view.
@@ -97,6 +100,7 @@ You can also set the pagination style on a per-view basis, using the `ListAPIVie
serializer_class = ExampleModelSerializer
paginate_by = 10
paginate_by_param = 'page_size'
+ max_paginate_by = 100
Note that using a `paginate_by` value of `None` will turn off pagination for the view.
@@ -114,6 +118,9 @@ You can also override the name used for the object list field, by setting the `r
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.
+ from rest_framework import pagination
+ from rest_framework import serializers
+
class LinksSerializer(serializers.Serializer):
next = pagination.NextPageField(source='*')
prev = pagination.PreviousPageField(source='*')
@@ -135,7 +142,7 @@ To have your custom pagination serializer be used by default, use the `DEFAULT_P
Alternatively, to set your custom pagination serializer on a per-view basis, use the `pagination_serializer_class` attribute on a generic class based view:
- class PaginatedListView(ListAPIView):
+ class PaginatedListView(generics.ListAPIView):
model = ExampleModel
pagination_serializer_class = CustomPaginationSerializer
paginate_by = 10
diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md
index 5bd79a31..1030fcb6 100644
--- a/docs/api-guide/parsers.md
+++ b/docs/api-guide/parsers.md
@@ -34,9 +34,13 @@ The default set of parsers may be set globally, using the `DEFAULT_PARSER_CLASSE
)
}
-You can also set the renderers used for an individual view, or viewset,
+You can also set the parsers used for an individual view, or viewset,
using the `APIView` class based views.
+ from rest_framework.parsers import YAMLParser
+ from rest_framework.response import Response
+ from rest_framework.views import APIView
+
class ExampleView(APIView):
"""
A view that can accept POST requests with YAML content.
diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md
index c6372f98..a7bf1555 100644
--- a/docs/api-guide/permissions.md
+++ b/docs/api-guide/permissions.md
@@ -25,9 +25,17 @@ Object level permissions are run by REST framework's generic views when `.get_ob
As with view level permissions, an `exceptions.PermissionDenied` exception will be raised if the user is not allowed to act on the given object.
If you're writing your own views and want to enforce object level permissions,
-you'll need to explicitly call the `.check_object_permissions(request, obj)` method on the view at the point at which you've retrieved the object.
+or if you override the `get_object` method on a generic view, then you'll need to explicitly call the `.check_object_permissions(request, obj)` method on the view at the point at which you've retrieved the object.
+
This will either raise a `PermissionDenied` or `NotAuthenticated` exception, or simply return if the view has the appropriate permissions.
+For example:
+
+ def get_object(self):
+ obj = get_object_or_404(self.get_queryset())
+ self.check_object_permissions(self.request, obj)
+ return obj
+
## Setting the permission policy
The default permission policy may be set globally, using the `DEFAULT_PERMISSION_CLASSES` setting. For example.
@@ -47,6 +55,10 @@ If not specified, this setting defaults to allowing unrestricted access:
You can also set the authentication policy on a per-view, or per-viewset basis,
using the `APIView` class based views.
+ from rest_framework.permissions import IsAuthenticated
+ from rest_framework.responses import Response
+ from rest_framework.views import APIView
+
class ExampleView(APIView):
permission_classes = (IsAuthenticated,)
@@ -157,6 +169,8 @@ For more details see the [2.2 release announcement][2.2-announcement].
The following is an example of a permission class that checks the incoming request's IP address against a blacklist, and denies the request if the IP has been blacklisted.
+ from rest_framework import permissions
+
class BlacklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
@@ -198,6 +212,10 @@ The following third party packages are also available.
The [DRF Any Permissions][drf-any-permissions] packages provides a different permission behavior in contrast to REST framework. Instead of all specified permissions being required, only one of the given permissions has to be true in order to get access to the view.
+## Composed Permissions
+
+The [Composed Permissions][composed-permissions] package provides a simple way to define complex and multi-depth (with logic operators) permission objects, using small and reusable components.
+
[cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html
[authentication]: authentication.md
[throttling]: throttling.md
@@ -208,3 +226,4 @@ The [DRF Any Permissions][drf-any-permissions] packages provides a different per
[2.2-announcement]: ../topics/2.2-announcement.md
[filtering]: filtering.md
[drf-any-permissions]: https://github.com/kevin-brown/drf-any-permissions
+[composed-permissions]: https://github.com/niwibe/djangorestframework-composed-permissions
diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md
index 829a3c54..5ec4b22f 100644
--- a/docs/api-guide/relations.md
+++ b/docs/api-guide/relations.md
@@ -76,7 +76,7 @@ This field is read only.
For example, the following serializer:
class AlbumSerializer(serializers.ModelSerializer):
- tracks = PrimaryKeyRelatedField(many=True, read_only=True)
+ tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Album
@@ -110,8 +110,8 @@ By default this field is read-write, although you can change this behavior using
For example, the following serializer:
class AlbumSerializer(serializers.ModelSerializer):
- tracks = HyperlinkedRelatedField(many=True, read_only=True,
- view_name='track-detail')
+ tracks = serializers.HyperlinkedRelatedField(many=True, read_only=True,
+ view_name='track-detail')
class Meta:
model = Album
@@ -148,7 +148,8 @@ By default this field is read-write, although you can change this behavior using
For example, the following serializer:
class AlbumSerializer(serializers.ModelSerializer):
- tracks = SlugRelatedField(many=True, read_only=True, slug_field='title')
+ tracks = serializers.SlugRelatedField(many=True, read_only=True,
+ slug_field='title')
class Meta:
model = Album
@@ -183,7 +184,7 @@ When using `SlugRelatedField` as a read-write field, you will normally want to e
This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer. It can also be used for an attribute on the object. For example, the following serializer:
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
- track_listing = HyperlinkedIdentityField(view_name='track-list')
+ track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')
class Meta:
model = Album
@@ -213,8 +214,6 @@ Nested relationships can be expressed by using serializers as fields.
If the field is used to represent a to-many relationship, you should add the `many=True` flag to the serializer field.
-Note that nested relationships are currently read-only. For read-write relationships, you should use a flat relational style.
-
## Example
For example, the following serializer:
@@ -422,7 +421,7 @@ For example, if all your object URLs used both a account and a slug in the the U
def get_object(self, queryset, view_name, view_args, view_kwargs):
account = view_kwargs['account']
slug = view_kwargs['slug']
- return queryset.get(account=account, slug=sug)
+ return queryset.get(account=account, slug=slug)
---
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
index bb3d2015..657377d9 100644
--- a/docs/api-guide/renderers.md
+++ b/docs/api-guide/renderers.md
@@ -30,11 +30,16 @@ The default set of renderers may be set globally, using the `DEFAULT_RENDERER_CL
You can also set the renderers used for an individual view, or viewset,
using the `APIView` class based views.
+ from django.contrib.auth.models import User
+ from rest_framework.renderers import JSONRenderer, YAMLRenderer
+ from rest_framework.response import Response
+ from rest_framework.views import APIView
+
class UserCountView(APIView):
"""
- A view that returns the count of active users, in JSON or JSONp.
+ A view that returns the count of active users, in JSON or YAML.
"""
- renderer_classes = (JSONRenderer, JSONPRenderer)
+ renderer_classes = (JSONRenderer, YAMLRenderer)
def get(self, request, format=None):
user_count = User.objects.filter(active=True).count()
@@ -83,7 +88,7 @@ The client may additionally include an `'indent'` media type parameter, in which
**.format**: `'.json'`
-**.charset**: `utf-8`
+**.charset**: `None`
## UnicodeJSONRenderer
@@ -105,7 +110,7 @@ Both the `JSONRenderer` and `UnicodeJSONRenderer` styles conform to [RFC 4627][r
**.format**: `'.json'`
-**.charset**: `utf-8`
+**.charset**: `None`
## JSONPRenderer
@@ -207,6 +212,20 @@ You can use `TemplateHTMLRenderer` either to return regular HTML pages using RES
See also: `TemplateHTMLRenderer`
+## HTMLFormRenderer
+
+Renders data returned by a serializer into an HTML form. The output of this renderer does not include the enclosing `<form>` tags or an submit actions, as you'll probably need those to include the desired method and URL. Also note that the `HTMLFormRenderer` does not yet support including field error messages.
+
+Note that the template used by the `HTMLFormRenderer` class, and the context submitted to it **may be subject to change**. If you need to use this renderer class it is advised that you either make a local copy of the class and templates, or follow the release note on REST framework upgrades closely.
+
+**.media_type**: `text/html`
+
+**.format**: `'.form'`
+
+**.charset**: `utf-8`
+
+**.template**: `'rest_framework/form.html'`
+
## BrowsableAPIRenderer
Renders data into HTML for the Browsable API. This renderer will determine which other renderer would have been given highest priority, and use that to display an API style response within the HTML page.
@@ -217,6 +236,8 @@ Renders data into HTML for the Browsable API. This renderer will determine whic
**.charset**: `utf-8`
+**.template**: `'rest_framework/api.html'`
+
#### Customizing BrowsableAPIRenderer
By default the response content will be rendered with the highest priority renderer apart from `BrowseableAPIRenderer`. If you need to customize this behavior, for example to use HTML as the default return format, but use JSON in the browsable API, you can do so by overriding the `get_default_renderer()` method. For example:
@@ -290,12 +311,15 @@ By default renderer classes are assumed to be using the `UTF-8` encoding. To us
Note that if a renderer class returns a unicode string, then the response content will be coerced into a bytestring by the `Response` class, with the `charset` attribute set on the renderer used to determine the encoding.
-If the renderer returns a bytestring representing raw binary content, you should set a charset value of `None`, which will ensure the `Content-Type` header of the response will not have a `charset` value set. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.
+If the renderer returns a bytestring representing raw binary content, you should set a charset value of `None`, which will ensure the `Content-Type` header of the response will not have a `charset` value set.
+
+In some cases you may also want to set the `render_style` attribute to `'binary'`. Doing so will also ensure that the browsable API will not attempt to display the binary content as a string.
class JPEGRenderer(renderers.BaseRenderer):
media_type = 'image/jpeg'
format = 'jpg'
charset = None
+ render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data
diff --git a/docs/api-guide/requests.md b/docs/api-guide/requests.md
index 39a34fcf..0696fedf 100644
--- a/docs/api-guide/requests.md
+++ b/docs/api-guide/requests.md
@@ -117,7 +117,7 @@ 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.
+As REST framework's `Request` extends Django's `HttpRequest`, all the other standard attributes and methods are also available. For example the `request.META` and `request.session` dictionaries are available as normal.
Note that due to implementation reasons the `Request` class does not inherit from `HttpRequest` class, but instead extends the class using composition.
diff --git a/docs/api-guide/reverse.md b/docs/api-guide/reverse.md
index 94262366..383eca4c 100644
--- a/docs/api-guide/reverse.md
+++ b/docs/api-guide/reverse.md
@@ -27,13 +27,13 @@ Has the same behavior as [`django.core.urlresolvers.reverse`][reverse], except t
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
+ from django.utils.timezone import now
class APIRootView(APIView):
def get(self, request):
- year = datetime.datetime.now().year
+ year = now().year
data = {
...
'year-summary-url': reverse('year-summary', args=[year], request=request)
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 072a2e79..fb48197e 100644
--- a/docs/api-guide/routers.md
+++ b/docs/api-guide/routers.md
@@ -14,6 +14,8 @@ REST framework adds support for automatic URL routing to Django, and provides yo
Here's an example of a simple URL conf, that uses `DefaultRouter`.
+ from rest_framework import routers
+
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
@@ -40,6 +42,9 @@ The example above would generate the following URL patterns:
Any methods on the viewset decorated with `@link` or `@action` will also be routed.
For example, given a method like this on the `UserViewSet` class:
+ from myapp.permissions import IsAdminOrIsSelf
+ from rest_framework.decorators import action
+
@action(permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
@@ -120,6 +125,8 @@ The arguments to the `Route` named tuple are:
The following example will only route to the `list` and `retrieve` actions, and does not use the trailing slash convention.
+ from rest_framework.routers import Route, SimpleRouter
+
class ReadOnlyRouter(SimpleRouter):
"""
A router for read-only APIs, which doesn't use trailing slashes.
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index bbc8d019..a3cd1d6a 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -28,6 +28,8 @@ We'll declare a serializer that we can use to serialize and deserialize `Comment
Declaring a serializer looks very similar to declaring a form:
+ from rest_framework import serializers
+
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
@@ -59,6 +61,8 @@ We can now use `CommentSerializer` to serialize a comment, or list of comments.
At this point we've translated the model instance into Python native datatypes. To finalise the serialization process we render the data into `json`.
+ from rest_framework.renderers import JSONRenderer
+
json = JSONRenderer().render(serializer.data)
json
# '{"email": "leila@example.com", "content": "foo bar", "created": "2012-08-22T16:20:09.822"}'
@@ -67,6 +71,9 @@ At this point we've translated the model instance into Python native datatypes.
Deserialization is similar. First we parse a stream into Python native datatypes...
+ from StringIO import StringIO
+ from rest_framework.parsers import JSONParser
+
stream = StringIO(json)
data = JSONParser().parse(stream)
@@ -177,7 +184,7 @@ If a nested representation may optionally accept the `None` value you should pas
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
-Similarly if a nested representation should be a list of items, you should the `many=True` flag to the nested serialized.
+Similarly if a nested representation should be a list of items, you should pass the `many=True` flag to the nested serialized.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
@@ -185,11 +192,13 @@ Similarly if a nested representation should be a list of items, you should the `
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
----
-
-**Note**: Nested serializers are only suitable for read-only representations, as there are cases where they would have ambiguous or non-obvious behavior if used when updating instances. For read-write representations you should always use a flat representation, by using one of the `RelatedField` subclasses.
+Validation of nested objects will work the same as before. Errors with nested objects will be nested under the field name of the nested object.
----
+ serializer = CommentSerializer(comment, data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
+ serializer.is_valid()
+ # False
+ serializer.errors
+ # {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}
## Dealing with multiple objects
@@ -241,7 +250,7 @@ This allows you to write views that update or create multiple items when a `PUT`
serializer = BookSerializer(queryset, data=data, many=True)
serializer.is_valid()
# True
- serialize.save() # `.save()` will be called on each updated or newly created instance.
+ serializer.save() # `.save()` will be called on each updated or newly created instance.
By default bulk updates will be limited to updating instances that already exist in the provided queryset.
@@ -253,7 +262,7 @@ When performing a bulk update you may want to allow new items to be created, and
serializer.save() # `.save()` will be called on updated or newly created instances.
# `.delete()` will be called on any other items in the `queryset`.
-Passing `allow_add_remove=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects.
+Passing `allow_add_remove=True` ensures that any update operations will completely overwrite the existing queryset, rather than simply updating existing objects.
#### How identity is determined when performing bulk updates
@@ -293,8 +302,7 @@ You can provide arbitrary additional context by passing a `context` argument whe
The context dictionary can be used within any serializer field logic, such as a custom `.to_native()` method, by accessing the `self.context` attribute.
----
-
+-
# ModelSerializer
Often you'll want serializer classes that map closely to model definitions.
@@ -337,6 +345,8 @@ The default `ModelSerializer` uses primary keys for relationships, but you can a
The `depth` option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
+If you want to customize the way the serialization is done (e.g. using `allow_add_remove`) you'll need to define the field yourself.
+
## Specifying which fields should be read-only
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the `read_only_fields` Meta option, like so:
diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md
index 0be0eb24..13f96f9a 100644
--- a/docs/api-guide/settings.md
+++ b/docs/api-guide/settings.md
@@ -25,7 +25,7 @@ If you need to access the values of REST framework's API settings in your projec
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 fall back 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.
@@ -127,6 +127,35 @@ Default: `None`
The name of a query parameter, which can be used by the client to override the default page size to use for pagination. If set to `None`, clients may not override the default page size.
+For example, given the following settings:
+
+ REST_FRAMEWORK = {
+ 'PAGINATE_BY': 10,
+ 'PAGINATE_BY_PARAM': 'page_size',
+ }
+
+A client would be able to modify the pagination size by using the `page_size` query parameter. For example:
+
+ GET http://example.com/api/accounts?page_size=25
+
+Default: `None`
+
+#### MAX_PAGINATE_BY
+
+The maximum page size to allow when the page size is specified by the client. If set to `None`, then no maximum limit is applied.
+
+For example, given the following settings:
+
+ REST_FRAMEWORK = {
+ 'PAGINATE_BY': 10,
+ 'PAGINATE_BY_PARAM': 'page_size',
+ 'MAX_PAGINATE_BY': 100
+ }
+
+A client request like the following would return a paginated list of up to 100 items.
+
+ GET http://example.com/api/accounts?page_size=999
+
Default: `None`
---
@@ -274,8 +303,56 @@ Default: `['iso-8601']`
---
+## View names and descriptions
+
+**The following settings are used to generate the view names and descriptions, as used in responses to `OPTIONS` requests, and as used in the browsable API.**
+
+#### VIEW_NAME_FUNCTION
+
+A string representing the function that should be used when generating view names.
+
+This should be a function with the following signature:
+
+ view_name(cls, suffix=None)
+
+* `cls`: The view class. Typically the name function would inspect the name of the class when generating a descriptive name, by accessing `cls.__name__`.
+* `suffix`: The optional suffix used when differentiating individual views in a viewset.
+
+Default: `'rest_framework.views.get_view_name'`
+
+#### VIEW_DESCRIPTION_FUNCTION
+
+A string representing the function that should be used when generating view descriptions.
+
+This setting can be changed to support markup styles other than the default markdown. For example, you can use it to support `rst` markup in your view docstrings being output in the browsable API.
+
+This should be a function with the following signature:
+
+ view_description(cls, html=False)
+
+* `cls`: The view class. Typically the description function would inspect the docstring of the class when generating a description, by accessing `cls.__doc__`
+* `html`: A boolean indicating if HTML output is required. `True` when used in the browsable API, and `False` when used in generating `OPTIONS` responses.
+
+Default: `'rest_framework.views.get_view_description'`
+
+---
+
## Miscellaneous settings
+#### EXCEPTION_HANDLER
+
+A string representing the function that should be used when returning a response for any given exception. If the function returns `None`, a 500 error will be raised.
+
+This setting can be changed to support error responses other than the default `{"detail": "Failure..."}` responses. For example, you can use it to provide API responses like `{"errors": [{"message": "Failure...", "code": ""} ...]}`.
+
+This should be a function with the following signature:
+
+ exception_handler(exc)
+
+* `exc`: The exception.
+
+Default: `'rest_framework.views.exception_handler'`
+
#### FORMAT_SUFFIX_KWARG
The name of a parameter in the URL conf that may be used to provide a format suffix.
diff --git a/docs/api-guide/status-codes.md b/docs/api-guide/status-codes.md
index db2e059c..409f659b 100644
--- a/docs/api-guide/status-codes.md
+++ b/docs/api-guide/status-codes.md
@@ -9,6 +9,7 @@
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
+ from rest_framework.response import Response
def empty_view(self):
content = {'please move along': 'nothing to see here'}
diff --git a/docs/api-guide/testing.md b/docs/api-guide/testing.md
index 92f8d54a..35c1f766 100644
--- a/docs/api-guide/testing.md
+++ b/docs/api-guide/testing.md
@@ -2,7 +2,7 @@
# Testing
-> Code without tests is broken as designed
+> Code without tests is broken as designed.
>
> &mdash; [Jacob Kaplan-Moss][cite]
@@ -16,6 +16,8 @@ Extends [Django's existing `RequestFactory` class][requestfactory].
The `APIRequestFactory` class supports an almost identical API to Django's standard `RequestFactory` class. This means the that standard `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` and `.options()` methods are all available.
+ from rest_framework.test import APIRequestFactory
+
# Using the standard RequestFactory API to create a form POST request
factory = APIRequestFactory()
request = factory.post('/notes/', {'title': 'new idea'})
@@ -49,6 +51,8 @@ For example, using `APIRequestFactory`, you can make a form PUT request like so:
Using Django's `RequestFactory`, you'd need to explicitly encode the data yourself:
+ from django.test.client import encode_multipart, RequestFactory
+
factory = RequestFactory()
data = {'title': 'remember to email dave'}
content = encode_multipart('BoUnDaRyStRiNg', data)
@@ -72,6 +76,12 @@ To forcibly authenticate a request, use the `force_authenticate()` method.
The signature for the method is `force_authenticate(request, user=None, token=None)`. When making the call, either or both of the user and token may be set.
+For example, when forcibly authenticating using a token, you might do something like the following:
+
+ user = User.objects.get(username='olivia')
+ request = factory.get('/accounts/django-superstars/')
+ force_authenticate(request, user=user, token=user.token)
+
---
**Note**: When using `APIRequestFactory`, the object that is returned is Django's standard `HttpRequest`, and not REST framework's `Request` object, which is only generated once the view is called.
@@ -105,6 +115,8 @@ Extends [Django's existing `Client` class][client].
The `APIClient` class supports the same request interface as `APIRequestFactory`. This means the that standard `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` and `.options()` methods are all available. For example:
+ from rest_framework.test import APIClient
+
client = APIClient()
client.post('/notes/', {'title': 'new idea'}, format='json')
@@ -131,8 +143,11 @@ The `login` method is appropriate for testing APIs that use session authenticati
The `credentials` method can be used to set headers that will then be included on all subsequent requests by the test client.
+ from rest_framework.authtoken.models import Token
+ from rest_framework.test import APIClient
+
# Include an appropriate `Authorization:` header on all requests.
- token = Token.objects.get(username='lauren')
+ token = Token.objects.get(user__username='lauren')
client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)
@@ -190,10 +205,10 @@ You can use any of REST framework's test case classes as you would for the regul
Ensure we can create a new account object.
"""
url = reverse('account-list')
- data = {'name': 'DabApps'}
+ expected = {'name': 'DabApps'}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(response.data, data)
+ self.assertEqual(response.data, expected)
---
diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md
index 56f32f58..cc469217 100644
--- a/docs/api-guide/throttling.md
+++ b/docs/api-guide/throttling.md
@@ -43,6 +43,10 @@ The rate descriptions used in `DEFAULT_THROTTLE_RATES` may include `second`, `mi
You can also set the throttling policy on a per-view or per-viewset basis,
using the `APIView` class based views.
+ from rest_framework.response import Response
+ from rest_framework.throttling import UserRateThrottle
+ from rest_framework.views import APIView
+
class ExampleView(APIView):
throttle_classes = (UserRateThrottle,)
@@ -66,6 +70,13 @@ Or, if you're using the `@api_view` decorator with function based views.
The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate [cache settings][cache-setting]. The default value of `LocMemCache` backend should be okay for simple setups. See Django's [cache documentation][cache-docs] for more details.
+If you need to use a cache other than `'default'`, you can do so by creating a custom throttle class and setting the `cache` attribute. For example:
+
+ class CustomAnonRateThrottle(AnonRateThrottle):
+ cache = get_cache('alternate')
+
+You'll need to rememeber to also set your custom throttle class in the `'DEFAULT_THROTTLE_CLASSES'` settings key, or using the `throttle_classes` view attribute.
+
---
# API Reference
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index 0c68afb0..2e65b7a4 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -19,6 +19,12 @@ Typically, rather than explicitly registering the views in a viewset in the urlc
Let's define a simple viewset that can be used to list or retrieve all the users in the system.
+ from django.contrib.auth.models import User
+ from django.shortcuts import get_object_or_404
+ from myapps.serializers import UserSerializer
+ from rest_framework import viewsets
+ from rest_framewor.responses import Response
+
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet that for listing or retrieving users.
@@ -41,6 +47,9 @@ If we need to, we can bind this viewset into two separate views, like so:
Typically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.
+ from myapp.views import UserViewSet
+ from rest_framework.routers import DefaultRouter
+
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls
@@ -133,6 +142,10 @@ The `@action` decorator will route `POST` requests by default, but may also acce
@action(methods=['POST', 'DELETE'])
def unset_password(self, request, pk=None):
...
+
+The two new actions will then be available at the urls `^users/{pk}/set_password/$` and `^users/{pk}/unset_password/$`
+
+
---
# API Reference