aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rwxr-xr-xdocs/api-guide/authentication.md17
-rw-r--r--docs/api-guide/fields.md3
-rw-r--r--docs/api-guide/routers.md117
-rw-r--r--docs/api-guide/settings.md6
-rw-r--r--docs/api-guide/throttling.md13
-rw-r--r--docs/api-guide/viewsets.md26
-rw-r--r--docs/index.md16
-rw-r--r--docs/topics/2.4-accouncement.md5
-rw-r--r--docs/topics/contributing.md2
-rw-r--r--docs/topics/release-notes.md26
-rw-r--r--docs/tutorial/6-viewsets-and-routers.md8
11 files changed, 181 insertions, 58 deletions
diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md
index 1cb37d67..343466ee 100755
--- a/docs/api-guide/authentication.md
+++ b/docs/api-guide/authentication.md
@@ -126,7 +126,13 @@ To use the `TokenAuthentication` scheme you'll need to [configure the authentica
'rest_framework.authtoken'
)
-Make sure to run `manage.py syncdb` after changing your settings. The `authtoken` database tables are managed by south (see [Schema migrations](#schema-migrations) below).
+
+---
+
+**Note:** Make sure to run `manage.py syncdb` after changing your settings. The `rest_framework.authtoken` app provides both Django (from v1.7) and South database migrations. See [Schema migrations](#schema-migrations) below.
+
+---
+
You'll also need to create tokens for your users.
@@ -198,7 +204,14 @@ Note that the default `obtain_auth_token` view explicitly uses JSON requests and
#### Schema migrations
-The `rest_framework.authtoken` app includes a south migration that will create the authtoken table.
+The `rest_framework.authtoken` app includes both Django native migrations (for Django versions >1.7) and South migrations (for Django versions <1.7) that will create the authtoken table.
+
+----
+
+**Note**: From REST Framework v2.4.0 using South with Django <1.7 requires upgrading South v1.0+
+
+----
+
If you're using a [custom user model][custom-user-model] you'll need to make sure that any initial migration that creates the user table runs before the authtoken table is created.
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index b41e0ebc..95d9fad3 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -164,11 +164,12 @@ 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`.
+If `allow_none` is `False` (default), `None` values will be converted to an empty string.
Corresponds to `django.db.models.fields.CharField`
or `django.db.models.fields.TextField`.
-**Signature:** `CharField(max_length=None, min_length=None)`
+**Signature:** `CharField(max_length=None, min_length=None, allow_none=False)`
## URLField
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 64f05af3..2d760ca4 100644
--- a/docs/api-guide/routers.md
+++ b/docs/api-guide/routers.md
@@ -51,36 +51,41 @@ This means you'll need to explicitly set the `base_name` argument when registeri
### Extra link and actions
-Any methods on the viewset decorated with `@link` or `@action` will also be routed.
+Any methods on the viewset decorated with `@detail_route` or `@list_route` 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):
+ from myapp.permissions import IsAdminOrIsSelf
+ from rest_framework.decorators import detail_route
+
+ class UserViewSet(ModelViewSet):
...
+
+ @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
+ def set_password(self, request, pk=None):
+ ...
The following URL pattern would additionally be generated:
* URL pattern: `^users/{pk}/set_password/$` Name: `'user-set-password'`
+For more information see the viewset documentation on [marking extra actions for routing][route-decorators].
+
# API Guide
## SimpleRouter
-This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@link` or `@action` decorators.
+This router includes routes for the standard set of `list`, `create`, `retrieve`, `update`, `partial_update` and `destroy` actions. The viewset can also mark additional methods to be routed, using the `@detail_route` or `@list_route` decorators.
<table border=1>
<tr><th>URL Style</th><th>HTTP Method</th><th>Action</th><th>URL Name</th></tr>
<tr><td rowspan=2>{prefix}/</td><td>GET</td><td>list</td><td rowspan=2>{basename}-list</td></tr></tr>
<tr><td>POST</td><td>create</td></tr>
+ <tr><td>{prefix}/{methodname}/</td><td>GET, or as specified by `methods` argument</td><td>`@list_route` decorated method</td><td>{basename}-{methodname}</td></tr>
<tr><td rowspan=4>{prefix}/{lookup}/</td><td>GET</td><td>retrieve</td><td rowspan=4>{basename}-detail</td></tr></tr>
<tr><td>PUT</td><td>update</td></tr>
<tr><td>PATCH</td><td>partial_update</td></tr>
<tr><td>DELETE</td><td>destroy</td></tr>
- <tr><td rowspan=2>{prefix}/{lookup}/{methodname}/</td><td>GET</td><td>@link decorated method</td><td rowspan=2>{basename}-{methodname}</td></tr>
- <tr><td>POST</td><td>@action decorated method</td></tr>
+ <tr><td>{prefix}/{lookup}/{methodname}/</td><td>GET, or as specified by `methods` argument</td><td>`@detail_route` decorated method</td><td>{basename}-{methodname}</td></tr>
</table>
By default the URLs created by `SimpleRouter` are appended with a trailing slash.
@@ -90,6 +95,12 @@ This behavior can be modified by setting the `trailing_slash` argument to `False
Trailing slashes are conventional in Django, but are not used by default in some other frameworks such as Rails. Which style you choose to use is largely a matter of preference, although some javascript frameworks may expect a particular routing style.
+The router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the `lookup_value_regex` attribute on the viewset. For example, you can limit the lookup to valid UUIDs:
+
+ class MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
+ lookup_field = 'my_model_id'
+ lookup_value_regex = '[0-9a-f]{32}'
+
## DefaultRouter
This router is similar to `SimpleRouter` as above, but additionally includes a default API root view, that returns a response containing hyperlinks to all the list views. It also generates routes for optional `.json` style format suffixes.
@@ -99,12 +110,12 @@ This router is similar to `SimpleRouter` as above, but additionally includes a d
<tr><td>[.format]</td><td>GET</td><td>automatically generated root view</td><td>api-root</td></tr></tr>
<tr><td rowspan=2>{prefix}/[.format]</td><td>GET</td><td>list</td><td rowspan=2>{basename}-list</td></tr></tr>
<tr><td>POST</td><td>create</td></tr>
+ <tr><td>{prefix}/{methodname}/[.format]</td><td>GET, or as specified by `methods` argument</td><td>`@list_route` decorated method</td><td>{basename}-{methodname}</td></tr>
<tr><td rowspan=4>{prefix}/{lookup}/[.format]</td><td>GET</td><td>retrieve</td><td rowspan=4>{basename}-detail</td></tr></tr>
<tr><td>PUT</td><td>update</td></tr>
<tr><td>PATCH</td><td>partial_update</td></tr>
<tr><td>DELETE</td><td>destroy</td></tr>
- <tr><td rowspan=2>{prefix}/{lookup}/{methodname}/[.format]</td><td>GET</td><td>@link decorated method</td><td rowspan=2>{basename}-{methodname}</td></tr>
- <tr><td>POST</td><td>@action decorated method</td></tr>
+ <tr><td>{prefix}/{lookup}/{methodname}/[.format]</td><td>GET, or as specified by `methods` argument</td><td>`@detail_route` decorated method</td><td>{basename}-{methodname}</td></tr>
</table>
As with `SimpleRouter` the trailing slashes on the URL routes can be removed by setting the `trailing_slash` argument to `False` when instantiating the router.
@@ -133,28 +144,87 @@ The arguments to the `Route` named tuple are:
**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `suffix` argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links.
+## Customizing dynamic routes
+
+You can also customize how the `@list_route` and `@detail_route` decorators are routed.
+To route either or both of these decorators, include a `DynamicListRoute` and/or `DynamicDetailRoute` named tuple in the `.routes` list.
+
+The arguments to `DynamicListRoute` and `DynamicDetailRoute` are:
+
+**url**: A string representing the URL to be routed. May include the same format strings as `Route`, and additionally accepts the `{methodname}` and `{methodnamehyphen}` format strings.
+
+**name**: The name of the URL as used in `reverse` calls. May include the following format strings: `{basename}`, `{methodname}` and `{methodnamehyphen}`.
+
+**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view.
+
## Example
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
+ from rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter
- class ReadOnlyRouter(SimpleRouter):
+ class CustomReadOnlyRouter(SimpleRouter):
"""
A router for read-only APIs, which doesn't use trailing slashes.
"""
routes = [
- Route(url=r'^{prefix}$',
- mapping={'get': 'list'},
- name='{basename}-list',
- initkwargs={'suffix': 'List'}),
- Route(url=r'^{prefix}/{lookup}$',
- mapping={'get': 'retrieve'},
- name='{basename}-detail',
- initkwargs={'suffix': 'Detail'})
+ Route(
+ url=r'^{prefix}$',
+ mapping={'get': 'list'},
+ name='{basename}-list',
+ initkwargs={'suffix': 'List'}
+ ),
+ Route(
+ url=r'^{prefix}/{lookup}$',
+ mapping={'get': 'retrieve'},
+ name='{basename}-detail',
+ initkwargs={'suffix': 'Detail'}
+ ),
+ DynamicDetailRoute(
+ url=r'^{prefix}/{lookup}/{methodnamehyphen}$',
+ name='{basename}-{methodnamehyphen}',
+ initkwargs={}
+ )
]
-The `SimpleRouter` class provides another example of setting the `.routes` attribute.
+Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a simple viewset.
+
+`views.py`:
+
+ class UserViewSet(viewsets.ReadOnlyModelViewSet):
+ """
+ A viewset that provides the standard actions
+ """
+ queryset = User.objects.all()
+ serializer_class = UserSerializer
+ lookup_field = 'username'
+
+ @detail_route()
+ def group_names(self, request):
+ """
+ Returns a list of all the group names that the given
+ user belongs to.
+ """
+ user = self.get_object()
+ groups = user.groups.all()
+ return Response([group.name for group in groups])
+
+`urls.py`:
+
+ router = CustomReadOnlyRouter()
+ router.register('users', UserViewSet)
+ urlpatterns = router.urls
+
+The following mappings would be generated...
+
+<table border=1>
+ <tr><th>URL</th><th>HTTP Method</th><th>Action</th><th>URL Name</th></tr>
+ <tr><td>/users</td><td>GET</td><td>list</td><td>user-list</td></tr>
+ <tr><td>/users/{username}</td><td>GET</td><td>retrieve</td><td>user-detail</td></tr>
+ <tr><td>/users/{username}/group-names</td><td>GET</td><td>group_names</td><td>user-group-names</td></tr>
+</table>
+
+For another example of setting the `.routes` attribute, see the source code for the `SimpleRouter` class.
## Advanced custom routers
@@ -184,6 +254,7 @@ The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (an
The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
[cite]: http://guides.rubyonrails.org/routing.html
+[route-decorators]: viewsets.html#marking-extra-actions-for-routing
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db]: http://wq.io/wq.db
[wq.db-router]: http://wq.io/docs/app.py
@@ -191,4 +262,4 @@ The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions
[drf-extensions-routers]: http://chibisov.github.io/drf-extensions/docs/#routers
[drf-extensions-nested-viewsets]: http://chibisov.github.io/drf-extensions/docs/#nested-routes
[drf-extensions-collection-level-controllers]: http://chibisov.github.io/drf-extensions/docs/#collection-level-controllers
-[drf-extensions-customizable-endpoint-names]: http://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name \ No newline at end of file
+[drf-extensions-customizable-endpoint-names]: http://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name
diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md
index c979019f..8bde4d87 100644
--- a/docs/api-guide/settings.md
+++ b/docs/api-guide/settings.md
@@ -377,5 +377,11 @@ The name of a parameter in the URL conf that may be used to provide a format suf
Default: `'format'`
+#### NUM_PROXIES
+
+An integer of 0 or more, that may be used to specify the number of application proxies that the API runs behind. This allows throttling to more accurately identify client IP addresses. If set to `None` then less strict IP matching will be used by the throttle classes.
+
+Default: `None`
+
[cite]: http://www.python.org/dev/peps/pep-0020/
[strftime]: http://docs.python.org/2/library/time.html#time.strftime
diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md
index 92f4c22b..832304f1 100644
--- a/docs/api-guide/throttling.md
+++ b/docs/api-guide/throttling.md
@@ -35,7 +35,7 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C
'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.
@@ -66,6 +66,16 @@ Or, if you're using the `@api_view` decorator with function based views.
}
return Response(content)
+## How clients are identified
+
+The `X-Forwarded-For` and `Remote-Addr` HTTP headers are used to uniquely identify client IP addresses for throttling. If the `X-Forwarded-For` header is present then it will be used, otherwise the value of the `Remote-Addr` header will be used.
+
+If you need to strictly identify unique client IP addresses, you'll need to first configure the number of application proxies that the API runs behind by setting the `NUM_PROXIES` setting. This setting should be an integer of zero or more. If set to non-zero then the client IP will be identified as being the last IP address in the `X-Forwarded-For` header, once any application proxy IP addresses have first been excluded. If set to zero, then the `Remote-Addr` header will always be used as the identifying IP address.
+
+It is important to understand that if you configure the `NUM_PROXIES` setting, then all clients behind a unique [NAT'd](http://en.wikipedia.org/wiki/Network_address_translation) gateway will be treated as a single client.
+
+Further context on how the `X-Forwarded-For` header works, and identifing a remote client IP can be [found here][identifing-clients].
+
## Setting up the cache
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.
@@ -178,5 +188,6 @@ The following is an example of a rate throttle, that will randomly throttle 1 in
[cite]: https://dev.twitter.com/docs/error-codes-responses
[permissions]: permissions.md
+[identifing-clients]: http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster
[cache-setting]: https://docs.djangoproject.com/en/dev/ref/settings/#caches
[cache-docs]: https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index aa2ceb7f..b32f5a80 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -70,7 +70,7 @@ There are two main advantages of using a `ViewSet` class over using a `View` cla
Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.
-## Marking extra methods for routing
+## Marking extra actions for routing
The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style operations, as shown below:
@@ -101,14 +101,16 @@ The default routers included with REST framework will provide routes for a stand
def destroy(self, request, pk=None):
pass
-If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the `@link` or `@action` decorators. The `@link` decorator will route `GET` requests, and the `@action` decorator will route `POST` requests.
+If you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the `@detail_route` or `@list_route` decorators.
+
+The `@detail_route` decorator contains `pk` in its URL pattern and is intended for methods which require a single instance. The `@list_route` decorator is intended for methods which operate on a list of objects.
For example:
from django.contrib.auth.models import User
- from rest_framework import viewsets
from rest_framework import status
- from rest_framework.decorators import action
+ from rest_framework import viewsets
+ from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
@@ -119,7 +121,7 @@ For example:
queryset = User.objects.all()
serializer_class = UserSerializer
- @action()
+ @detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.DATA)
@@ -131,21 +133,27 @@ For example:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
-The `@action` and `@link` decorators can additionally take extra arguments that will be set for the routed view only. For example...
+ @list_route()
+ def recent_users(self, request):
+ recent_users = User.objects.all().order('-last_login')
+ page = self.paginate_queryset(recent_users)
+ serializer = self.get_pagination_serializer(page)
+ return Response(serializer.data)
+
+The decorators can additionally take extra arguments that will be set for the routed view only. For example...
- @action(permission_classes=[IsAdminOrIsSelf])
+ @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The `@action` decorator will route `POST` requests by default, but may also accept other HTTP methods, by using the `methods` argument. For example:
- @action(methods=['POST', 'DELETE'])
+ @detail_route(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
diff --git a/docs/index.md b/docs/index.md
index dd407497..6abc4f04 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -207,19 +207,9 @@ General guides to using REST framework.
## Development
-If you want to work on REST framework itself, clone the repository, then...
-
-Build the docs:
-
- ./mkdocs.py
-
-Run the tests:
-
- ./rest_framework/runtests/runtests.py
-
-To run the tests against all supported configurations, first install [the tox testing tool][tox] globally, using `pip install tox`, then simply run `tox`:
-
- tox
+See the [Contribution guidelines][contributing] for information on how to clone
+the repository, run the test suite and contribute changes back to REST
+Framework.
## Support
diff --git a/docs/topics/2.4-accouncement.md b/docs/topics/2.4-accouncement.md
new file mode 100644
index 00000000..91472b9c
--- /dev/null
+++ b/docs/topics/2.4-accouncement.md
@@ -0,0 +1,5 @@
+* Writable nested serializers.
+* List/detail routes.
+* 1.3 Support dropped, install six for <=1.4.?.
+* `allow_none` for char fields
+* `trailing_slash = True` --> `[^/]`, `trailing_slash = False` --> `[^/.]`, becomes simply `[^/]` and `lookup_value_regex` is added.
diff --git a/docs/topics/contributing.md b/docs/topics/contributing.md
index 18a05050..d33843e1 100644
--- a/docs/topics/contributing.md
+++ b/docs/topics/contributing.md
@@ -65,7 +65,7 @@ To run the tests, clone the repository, and then:
pip install -r optionals.txt
# Run the tests
- rest_framework/runtests/runtests.py
+ py.test
You can also use the excellent [tox][tox] testing tool to run the tests against all supported versions of Python and Django. Install `tox` globally, and then simply run:
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index ea4c912c..b0e5b198 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -38,8 +38,26 @@ You can determine your currently installed version using `pip freeze`:
---
+### 2.4.0
+
+* Added compatibility with Django 1.7's native migrations.
+
+ **IMPORTANT**: In order to continue to use South with Django <1.7 you **must** upgrade to
+ South v1.0.
+
+* Use py.test
+* `@detail_route` and `@list_route` decorators replace `@action` and `@link`.
+* `six` no longer bundled. For Django <= 1.4.1, install `six` package.
+* Support customizable view name and description functions, using the `VIEW_NAME_FUNCTION` and `VIEW_DESCRIPTION_FUNCTION` settings.
+* Added `NUM_PROXIES` setting for smarter client IP identification.
+* Added `MAX_PAGINATE_BY` setting and `max_paginate_by` generic view attribute.
+* Added `cache` attribute to throttles to allow overriding of default cache.
+* Bugfix: `?page_size=0` query parameter now falls back to default page size for view, instead of always turning pagination off.
+
+
## 2.3.x series
+
### 2.3.14
**Date**: 12th June 2014
@@ -169,9 +187,9 @@ You can determine your currently installed version using `pip freeze`:
* Added `trailing_slash` option to routers.
* Include support for `HttpStreamingResponse`.
* Support wider range of default serializer validation when used with custom model fields.
-* UTF-8 Support for browsable API descriptions.
+* UTF-8 Support for browsable API descriptions.
* OAuth2 provider uses timezone aware datetimes when supported.
-* Bugfix: Return error correctly when OAuth non-existent consumer occurs.
+* Bugfix: Return error correctly when OAuth non-existent consumer occurs.
* Bugfix: Allow `FileUploadParser` to correctly filename if provided as URL kwarg.
* Bugfix: Fix `ScopedRateThrottle`.
@@ -212,7 +230,7 @@ You can determine your currently installed version using `pip freeze`:
* Added SearchFilter
* Added OrderingFilter
* Added GenericViewSet
-* Bugfix: Multiple `@action` and `@link` methods now allowed on viewsets.
+* Bugfix: Multiple `@action` and `@link` methods now allowed on viewsets.
* Bugfix: Fix API Root view issue with DjangoModelPermissions
### 2.3.2
@@ -265,7 +283,7 @@ You can determine your currently installed version using `pip freeze`:
* Long HTTP headers in browsable API are broken in multiple lines when possible.
* Bugfix: Fix regression with DjangoFilterBackend not worthing correctly with single object views.
* Bugfix: OAuth should fail hard when invalid token used.
-* Bugfix: Fix serializer potentially returning `None` object for models that define `__bool__` or `__len__`.
+* Bugfix: Fix serializer potentially returning `None` object for models that define `__bool__` or `__len__`.
### 2.2.5
diff --git a/docs/tutorial/6-viewsets-and-routers.md b/docs/tutorial/6-viewsets-and-routers.md
index 04b42f2e..b2019520 100644
--- a/docs/tutorial/6-viewsets-and-routers.md
+++ b/docs/tutorial/6-viewsets-and-routers.md
@@ -25,7 +25,7 @@ Here we've used the `ReadOnlyModelViewSet` class to automatically provide the de
Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighlight` view classes. We can remove the three views, and again replace them with a single class.
- from rest_framework.decorators import link
+ from rest_framework.decorators import detail_route
class SnippetViewSet(viewsets.ModelViewSet):
"""
@@ -39,7 +39,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
- @link(renderer_classes=[renderers.StaticHTMLRenderer])
+ @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
@@ -49,9 +49,9 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations.
-Notice that we've also used the `@link` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style.
+Notice that we've also used the `@detail_route` decorator to create a custom action, named `highlight`. This decorator can be used to add any custom endpoints that don't fit into the standard `create`/`update`/`delete` style.
-Custom actions which use the `@link` decorator will respond to `GET` requests. We could have instead used the `@action` decorator if we wanted an action that responded to `POST` requests.
+Custom actions which use the `@detail_route` decorator will respond to `GET` requests. We can use the `methods` argument if we wanted an action that responded to `POST` requests.
## Binding ViewSets to URLs explicitly