Update docs

This commit is contained in:
Ryan P Kilby 2018-01-04 03:20:43 -05:00
parent b69efe054e
commit 2112a58282
4 changed files with 66 additions and 70 deletions

View File

@ -67,7 +67,7 @@ If you have specific requirements for creating schema endpoints that are accesse
For example, the following additional route could be used on a viewset to provide a linkable schema endpoint.
@list_route(methods=['GET'])
@action(methods=['GET'], detail=False)
def schema(self, request):
meta = self.metadata_class()
data = meta.determine_metadata(request, self)

View File

@ -81,62 +81,45 @@ Router URL patterns can also be namespaces.
If using namespacing with hyperlinked serializers you'll also need to ensure that any `view_name` parameters on the serializers correctly reflect the namespace. In the example above you'd need to include a parameter such as `view_name='api:user-detail'` for serializer fields hyperlinked to the user detail view.
### Extra link and actions
### Routing for extra actions
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:
Any method on the viewset decorated with `@action` will be included in the generated routes. For example, given a method like this on the `UserViewSet` class:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route
from rest_framework.decorators import action
class UserViewSet(ModelViewSet):
...
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The following URL pattern would additionally be generated:
The following route would be generated:
* URL pattern: `^users/{pk}/set_password/$` Name: `'user-set-password'`
* URL pattern: `^users/{pk}/set_password/$`
* URL name: `'user-set-password'`
If you do not want to use the default URL generated for your custom action, you can instead use the url_path parameter to customize it.
By default, the URL pattern is based on the method name, and the URL name is the combination of the `ViewSet.basename` and the hyphenated method name.
If you don't want to use the default URL or default name, you can instead pass the `url_path` and `url_name` arguments to the `@action` decorator.
For example, if you want to change the URL for our custom action to `^users/{pk}/change-password/$`, you could write:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route
from rest_framework.decorators import action
class UserViewSet(ModelViewSet):
...
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password')
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],
url_path='change-password', url_name='change_password')
def set_password(self, request, pk=None):
...
The above example would now generate the following URL pattern:
* URL pattern: `^users/{pk}/change-password/$` Name: `'user-change-password'`
In the case you do not want to use the default name generated for your custom action, you can use the url_name parameter to customize it.
For example, if you want to change the name of our custom action to `'user-change-password'`, you could write:
from myapp.permissions import IsAdminOrIsSelf
from rest_framework.decorators import detail_route
class UserViewSet(ModelViewSet):
...
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password')
def set_password(self, request, pk=None):
...
The above example would now generate the following URL pattern:
* URL pattern: `^users/{pk}/set_password/$` Name: `'user-change-password'`
You can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views.
* URL path: `^users/{pk}/change-password/$`
* URL name: `'user-change_password'`
For more information see the viewset documentation on [marking extra actions for routing][route-decorators].
@ -144,18 +127,18 @@ For more information see the viewset documentation on [marking extra actions for
## 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 `@detail_route` or `@list_route` 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 `@action` decorator.
<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>{prefix}/{url_path}/</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=False)` decorated method</td><td>{basename}-{url_name}</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>{prefix}/{lookup}/{methodname}/</td><td>GET, or as specified by `methods` argument</td><td>`@detail_route` decorated method</td><td>{basename}-{methodname}</td></tr>
<tr><td>{prefix}/{lookup}/{url_path}/</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=True)` decorated method</td><td>{basename}-{url_name}</td></tr>
</table>
By default the URLs created by `SimpleRouter` are appended with a trailing slash.
@ -180,12 +163,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>{prefix}/{url_path}/[.format]</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=False)` decorated method</td><td>{basename}-{url_name}</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>{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>
<tr><td>{prefix}/{lookup}/{url_path}/[.format]</td><td>GET, or as specified by `methods` argument</td><td>`@action(detail=True)` decorated method</td><td>{basename}-{url_name}</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.
@ -212,18 +195,18 @@ The arguments to the `Route` named tuple are:
* `{basename}` - The base to use for the URL names that are created.
**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.
**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the `detail`, `basename`, and `suffix` arguments are reserved for viewset introspection and are also used by the browsable API to generate 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.
You can also customize how the `@action` decorator is routed. Include the `DynamicRoute` named tuple in the `.routes` list, setting the `detail` argument as appropriate for the list-based and detail-based routes. In addition to `detail`, the arguments to `DynamicRoute` are:
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 `{url_path}` format string.
**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:
**name**: The name of the URL as used in `reverse` calls. May include the following format strings: `{basename}`, `{methodname}` and `{methodnamehyphen}`.
* `{basename}` - The base to use for the URL names that are created.
* `{url_name}` - The `url_name` provided to the `@action`.
**initkwargs**: A dictionary of any additional arguments that should be passed when instantiating the view.
@ -231,7 +214,7 @@ The arguments to `DynamicListRoute` and `DynamicDetailRoute` 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, DynamicDetailRoute, SimpleRouter
from rest_framework.routers import Route, DynamicRoute, SimpleRouter
class CustomReadOnlyRouter(SimpleRouter):
"""
@ -250,9 +233,10 @@ The following example will only route to the `list` and `retrieve` actions, and
name='{basename}-detail',
initkwargs={'suffix': 'Detail'}
),
DynamicDetailRoute(
url=r'^{prefix}/{lookup}/{methodnamehyphen}$',
name='{basename}-{methodnamehyphen}',
DynamicRoute(
url=r'^{prefix}/{lookup}/{url_path}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
)
]
@ -269,7 +253,7 @@ Let's take a look at the routes our `CustomReadOnlyRouter` would generate for a
serializer_class = UserSerializer
lookup_field = 'username'
@detail_route()
@action(detail=True)
def group_names(self, request, pk=None):
"""
Returns a list of all the group names that the given

View File

@ -102,10 +102,16 @@ The default routers included with REST framework will provide routes for a stand
def destroy(self, request, pk=None):
pass
During dispatch the name of the current action is available via the `.action` attribute.
You may inspect `.action` to adjust behaviour based on the current action.
## Introspecting ViewSet actions
For example, you could restrict permissions to everything except the `list` action similar to this:
During dispatch, the following attributes are available on the `ViewSet`.
* `basename` - the base to use for the URL names that are created.
* `action` - the name of the current action (e.g., `list`, `create`).
* `detail` - boolean indicating if the current action is configured for a list or detail view.
* `suffix` - the display suffix for the viewset type - mirrors the `detail` attribute.
You may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the `list` action similar to this:
def get_permissions(self):
"""
@ -119,16 +125,13 @@ For example, you could restrict permissions to everything except the `list` acti
## Marking extra actions for routing
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.
If you have ad-hoc methods that should be routable, you can mark them as such with the `@action` decorator. Like regular actions, extra actions may be intended for either a list of objects, or a single instance. To indicate this, set the `detail` argument to `True` or `False`. The router will configure its URL patterns accordingly. e.g., the `DefaultRouter` will configure detail actions to contain `pk` in their URL patterns.
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:
A more complete example of extra actions:
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
@ -139,7 +142,7 @@ For example:
queryset = User.objects.all()
serializer_class = UserSerializer
@detail_route(methods=['post'])
@action(methods=['post'], detail=True)
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
@ -151,7 +154,7 @@ For example:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
@list_route()
@action(detail=False)
def recent_users(self, request):
recent_users = User.objects.all().order('-last_login')
@ -163,20 +166,22 @@ For example:
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
The decorators can additionally take extra arguments that will be set for the routed view only. For example...
The decorator can additionally take extra arguments that will be set for the routed view only. For example:
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
These decorators will route `GET` requests by default, but may also accept other HTTP methods, by using the `methods` argument. For example:
These decorator will route `GET` requests by default, but may also accept other HTTP methods by setting the `methods` argument. For example:
@detail_route(methods=['post', 'delete'])
@action(methods=['post', 'delete'], detail=True)
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/$`
To view all extra actions, call the `.get_extra_actions()` method.
## Reversing action URLs
If you need to get the URL of an action, use the `.reverse_action()` method. This is a convenience wrapper for `reverse()`, automatically passing the view's `request` object and prepending the `url_name` with the `.basename` attribute.
@ -190,7 +195,14 @@ Using the example from the previous section:
'http://localhost:8000/api/users/1/set_password'
```
The `url_name` argument should match the same argument to the `@list_route` and `@detail_route` decorators. Additionally, this can be used to reverse the default `list` and `detail` routes.
Alternatively, you can use the `url_name` attribute set by the `@action` decorator.
```python
>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
```
The `url_name` argument for `.reverse_action()` should match the same argument to the `@action` decorator. Additionally, this method can be used to reverse the default actions, such as `list` and `create`.
---

View File

@ -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 detail_route
from rest_framework.decorators import action
from rest_framework.response import Response
class SnippetViewSet(viewsets.ModelViewSet):
@ -40,7 +40,7 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
@detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
@action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
@ -50,11 +50,11 @@ 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 `@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.
Notice that we've also used the `@action` 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 `@detail_route` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests.
Custom actions which use the `@action` decorator will respond to `GET` requests by default. We can use the `methods` argument if we wanted an action that responded to `POST` requests.
The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include url_path as a decorator keyword argument.
The URLs for custom actions by default depend on the method name itself. If you want to change the way url should be constructed, you can include `url_path` as a decorator keyword argument.
## Binding ViewSets to URLs explicitly