aboutsummaryrefslogtreecommitdiffstats
path: root/docs/api-guide
diff options
context:
space:
mode:
authorTom Christie2013-09-25 09:44:26 +0100
committerTom Christie2013-09-25 09:44:26 +0100
commit21cd6386593aea0b122abec1c5cc3bd5c544aa87 (patch)
treeb7d197c9c04f56448bee36c4789c93c66fb541a8 /docs/api-guide
parent9a5b2eefa92dede844ab94d049093e91ac98af5b (diff)
parente8c6cd5622f62fcf2d4cf2b28b504fe5ff5228f9 (diff)
downloaddjango-rest-framework-21cd6386593aea0b122abec1c5cc3bd5c544aa87.tar.bz2
Merge master
Diffstat (limited to 'docs/api-guide')
-rwxr-xr-xdocs/api-guide/authentication.md2
-rw-r--r--docs/api-guide/exceptions.md47
-rw-r--r--docs/api-guide/filtering.md46
-rwxr-xr-xdocs/api-guide/generic-views.md2
-rw-r--r--docs/api-guide/permissions.md18
-rw-r--r--docs/api-guide/relations.md2
-rw-r--r--docs/api-guide/serializers.md2
-rw-r--r--docs/api-guide/settings.md16
-rw-r--r--docs/api-guide/viewsets.md2
9 files changed, 128 insertions, 9 deletions
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index f30b16ed..7caeac1e 100755
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -404,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/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/filtering.md b/docs/api-guide/filtering.md
index 649462da..859e8d52 100644
--- a/docs/api-guide/filtering.md
+++ b/docs/api-guide/filtering.md
@@ -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.
+ """
+ perms_map = {
+ 'GET': ['%(app_label)s.view_%(model_name)s'],
+ 'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
+ 'HEAD': ['%(app_label)s.view_%(model_name)s'],
+ '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'],
+ }
+
+**views.py**:
+
+ class EventViewSet(viewsets.ModelViewSet):
+ """
+ Viewset that only lists events if user has 'view' permissions, and only
+ allows operations on individual events if user has appropriate 'view', 'add',
+ 'change' or 'delete' permissions.
+ """
+ queryset = Event.objects.all()
+ serializer = EventSerializer
+ filter_backends = (filters.DjangoObjectPermissionsFilter,)
+ permission_classes = (myapp.permissions.CustomObjectPermissions,)
+
+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
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,
[cite]: https://docs.djangoproject.com/en/dev/topics/db/queries/#retrieving-specific-objects-with-filters
[django-filter]: https://github.com/alex/django-filter
[django-filter-docs]: https://django-filter.readthedocs.org/en/latest/index.html
+[guardian]: http://pythonhosted.org/django-guardian/
+[view-permissions]: http://pythonhosted.org/django-guardian/userguide/assign.html
+[view-permissions-blogpost]: http://blog.nyaruka.com/adding-a-view-permission-to-django-models
[nullbooleanselect]: https://github.com/django/django/blob/master/django/forms/widgets.py
[search-django-admin]: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
diff --git a/docs/api-guide/generic-views.md b/docs/api-guide/generic-views.md
index 7185b6b6..dc0076df 100755
--- a/docs/api-guide/generic-views.md
+++ b/docs/api-guide/generic-views.md
@@ -69,7 +69,7 @@ The following attributes control the basic view behavior.
**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.
**Pagination**:
diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md
index a7bf1555..871de84e 100644
--- a/docs/api-guide/permissions.md
+++ b/docs/api-guide/permissions.md
@@ -120,7 +120,21 @@ To use custom model permissions, override `DjangoModelPermissions` and set the `
## DjangoModelPermissionsOrAnonReadOnly
-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
@@ -220,7 +234,9 @@ The [Composed Permissions][composed-permissions] package provides a simple way t
[authentication]: authentication.md
[throttling]: throttling.md
[contribauth]: https://docs.djangoproject.com/en/1.0/topics/auth/#permissions
+[objectpermissions]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#handling-object-permissions
[guardian]: https://github.com/lukaszb/django-guardian
+[get_objects_for_user]: http://pythonhosted.org/django-guardian/api/guardian.shortcuts.html#get-objects-for-user
[django-oauth-plus]: http://code.larlet.fr/django-oauth-plus
[django-oauth2-provider]: https://github.com/caffeinehit/django-oauth2-provider
[2.2-announcement]: ../topics/2.2-announcement.md
diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md
index 15ba9a3a..5ec4b22f 100644
--- a/docs/api-guide/relations.md
+++ b/docs/api-guide/relations.md
@@ -421,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/serializers.md b/docs/api-guide/serializers.md
index 5d7e2ac8..a3cd1d6a 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -250,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.
diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md
index 542e8c5f..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.
@@ -339,6 +339,20 @@ 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/viewsets.md b/docs/api-guide/viewsets.md
index 6498e177..a5359e99 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -23,7 +23,7 @@ Let's define a simple viewset that can be used to list or retrieve all the users
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
- from rest_framewor.responses import Response
+ from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
"""