@@ -28,11 +28,54 @@ For example, the following request:
...
@@ -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:
Might receive an error response indicating that the `DELETE` method is not allowed on that resource:
HTTP/1.1 405 Method Not Allowed
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json; charset=utf-8
Content-Type: application/json
Content-Length: 42
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
{"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
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.
@@ -257,6 +257,49 @@ The `ordering` attribute may be either a string or a list/tuple of strings.
...
@@ -257,6 +257,49 @@ The `ordering` attribute may be either a string or a list/tuple of strings.
---
---
## DjangoObjectPermissionsFilter
The `DjangoObjectPermissionsFilter` is intended to be used together with the [`django-guardian`][guardian] package, with custom `'view'` permissions added. The filter will ensure that querysets only returns objects for which the user has the appropriate view permission.
This filter class must be used with views that provide either a `queryset` or a `model` attribute.
If you're using `DjangoObjectPermissionsFilter`, you'll probably also want to add an appropriate object permissions class, to ensure that users can only operate on instances if they have the appropriate object permissions. The easiest way to do this is to subclass `DjangoObjectPermissions` and add `'view'` permissions to the `perms_map` attribute.
A complete example using both `DjangoObjectPermissionsFilter` and `DjangoObjectPermissions` might look something like this.
**permissions.py**:
class CustomObjectPermissions(permissions.DjangoObjectPermissions):
"""
Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
For more information on adding `'view'` permissions for models, see the [relevant section][view-permissions] of the `django-guardian` documentation, and [this blogpost][view-permissions-blogpost].
---
# Custom generic filtering
# Custom generic filtering
You can also provide your own generic filtering backend, or write an installable app for other developers to use.
You can also provide your own generic filtering backend, or write an installable app for other developers to use.
...
@@ -281,5 +324,8 @@ We could achieve the same behavior by overriding `get_queryset()` on the views,
...
@@ -281,5 +324,8 @@ We could achieve the same behavior by overriding `get_queryset()` on the views,
@@ -69,7 +69,7 @@ The following attributes control the basic view behavior.
...
@@ -69,7 +69,7 @@ The following attributes control the basic view behavior.
**Shortcuts**:
**Shortcuts**:
*`model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class.
*`model` - This shortcut may be used instead of setting either (or both) of the `queryset`/`serializer_class` attributes, although using the explicit style is generally preferred. If used instead of `serializer_class`, then then `DEFAULT_MODEL_SERIALIZER_CLASS` setting will determine the base serializer class. Note that `model` is only ever used for generating a default queryset or serializer class - the `queryset` and `serializer_class` attributes are always preferred if provided.
@@ -122,6 +122,20 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `
...
@@ -122,6 +122,20 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `
Similar to `DjangoModelPermissions`, but also allows unauthenticated users to have read-only access to the API.
Similar to `DjangoModelPermissions`, but also allows unauthenticated users to have read-only access to the API.
## DjangoObjectPermissions
This permission class ties into Django's standard [object permissions framework][objectpermissions] that allows per-object permissions on models. In order to use this permission class, you'll also need to add a permission backend that supports object-level permissions, such as [django-guardian][guardian].
When applied to a view that has a `.model` property, authorization will only be granted if the user *is authenticated* and has the *relevant per-object permissions* and *relevant model permissions* assigned.
*`POST` requests require the user to have the `add` permission on the model instance.
*`PUT` and `PATCH` requests require the user to have the `change` permission on the model instance.
*`DELETE` requests require the user to have the `delete` permission on the model instance.
Note that `DjangoObjectPermissions` **does not** require the `django-guardian` package, and should support other object-level backends equally well.
As with `DjangoModelPermissions` you can use custom model permissions by overriding `DjangoModelPermissions` and setting the `.perms_map` property. Refer to the source code for details. Note that if you add a custom `view` permission for `GET`, `HEAD` and `OPTIONS` requests, you'll probably also want to consider adding the `DjangoObjectPermissionsFilter` class to ensure that list endpoints only return results including objects for which the user has appropriate view permissions.
## TokenHasReadWriteScope
## TokenHasReadWriteScope
This permission class is intended for use with either of the `OAuthAuthentication` and `OAuth2Authentication` classes, and ties into the scoping that their backends provide.
This permission class is intended for use with either of the `OAuthAuthentication` and `OAuth2Authentication` classes, and ties into the scoping that their backends provide.
...
@@ -220,7 +234,9 @@ The [Composed Permissions][composed-permissions] package provides a simple way t
...
@@ -220,7 +234,9 @@ The [Composed Permissions][composed-permissions] package provides a simple way t
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:
**Note**: The `oauth2` Python package is badly misnamed, and actually provides OAuth 1.0a support. Also note that packages required for both OAuth 1.0a, and OAuth 2.0 are not yet Python 3 compatible.
**Note**: The `oauth2` Python package is badly misnamed, and actually provides OAuth 1.0a support. Also note that packages required for both OAuth 1.0a, and OAuth 2.0 are not yet Python 3 compatible.
...
@@ -250,6 +251,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
...
@@ -250,6 +251,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -115,6 +115,7 @@ The context that's available to the template:
...
@@ -115,6 +115,7 @@ The context that's available to the template:
*`name` : The name of the resource
*`name` : The name of the resource
*`post_form` : A form instance for use by the POST form (if allowed)
*`post_form` : A form instance for use by the POST form (if allowed)
*`put_form` : A form instance for use by the PUT form (if allowed)
*`put_form` : A form instance for use by the PUT form (if allowed)
*`display_edit_forms` : A boolean indicating whether or not POST, PUT and PATCH forms will be displayed
*`request` : The request object
*`request` : The request object
*`response` : The response object
*`response` : The response object
*`version` : The version of Django REST Framework
*`version` : The version of Django REST Framework
...
@@ -122,6 +123,8 @@ The context that's available to the template:
...
@@ -122,6 +123,8 @@ The context that's available to the template:
*`FORMAT_PARAM` : The view can accept a format override
*`FORMAT_PARAM` : The view can accept a format override
*`METHOD_PARAM` : The view can accept a method override
*`METHOD_PARAM` : The view can accept a method override
You can override the `BrowsableAPIRenderer.get_context()` method to customise the context that gets passed to the template.
#### Not using base.html
#### Not using base.html
For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you.
For more advanced customization, such as not having a Bootstrap basis or tighter integration with the rest of your site, you can simply choose not to have `api.html` extend `base.html`. Then the page content and capabilities are entirely up to you.
> To save HTTP requests, it may be convenient to send related documents along with the request.
>
> — [JSON API specification for Ember Data][cite].
# Writable nested serializers
Although flat data structures serve to properly delineate between the individual entities in your service, there are cases where it may be more appropriate or convenient to use nested data structures.
Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go. However, there are a few more subtleties to using writable nested serializers, due to the dependancies between the various model instances, and the need to save or delete multiple instances in a single action.
## One-to-many data structures
*Example of a **read-only** nested serializer. Nothing complex to worry about here.*
class ToDoItemSerializer(serializers.ModelSerializer):
class Meta:
model = ToDoItem
fields = ('text', 'is_completed')
class ToDoListSerializer(serializers.ModelSerializer):