diff options
Diffstat (limited to 'docs/api-guide')
| -rw-r--r-- | docs/api-guide/fields.md | 3 | ||||
| -rw-r--r-- | docs/api-guide/routers.md | 115 | ||||
| -rw-r--r-- | docs/api-guide/settings.md | 6 | ||||
| -rw-r--r-- | docs/api-guide/throttling.md | 13 | ||||
| -rw-r--r-- | docs/api-guide/viewsets.md | 28 | 
5 files changed, 131 insertions, 34 deletions
| diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 67fa65d2..883734a3 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 7efc140a..819cf980 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 @@ -180,6 +250,7 @@ The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (an      app.router.register_model(MyModel)  [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 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 b7c320f0..d223f9b3 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 23b16575..161fd2a9 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 `method` argument.  For example: +By default, the decorators will route `GET` requests, 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 | 
