From 6fdeed2e2c2f385e99a5fb42f169526a229bfa4b Mon Sep 17 00:00:00 2001
From: Carlton Gibson The Note that the default Note that the default By default there are no permissions or throttling applied to the If you need a customized version of the For example, you may return additional user information beyond the And in your It is also possible to create Tokens manually through admin interface. In case you are using a large user base, we recommend that you monkey patch the The generic views use the By default this exception results in a response with the HTTP status code "400 Bad Request". Django REST Framework provides two error views suitable for providing generic JSON Use these as per Django's Customizing error views documentation. Returns a response with status code Set as Returns a response with status code Set as obtain_auth_token view will return a JSON response when valid username and password fields are POSTed to the view using form data or JSON:
-{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
obtain_auth_token view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings. If you need a customized version of the obtain_auth_token view, you can do so by overriding the ObtainAuthToken view class, and using that in your url conf instead.obtain_auth_token view explicitly uses JSON requests and responses, rather than using default renderer and parser classes in your settings.obtain_auth_token view. If you do wish to apply throttling you'll need to override the view class,
and include them using the throttle_classes attribute.obtain_auth_token view, you can do so by subclassing the ObtainAuthToken view class, and using that in your url conf instead.token value:
+from rest_framework.authtoken.views import ObtainAuthToken
+from rest_framework.authtoken.models import Token
+from rest_framework.response import Response
+
+class CustomAuthToken(ObtainAuthToken):
+
+ def post(self, request, *args, **kwargs):
+ serializer = self.serializer_class(data=request.data,
+ context={'request': request})
+ serializer.is_valid(raise_exception=True)
+ user = serializer.validated_data['user']
+ token, created = Token.objects.get_or_create(user=user)
+ return Response({
+ 'token': token.key,
+ 'user_id': user.pk,
+ 'email': user.email
+ })
+urls.py:urlpatterns += [
+ url(r'^api-token-auth/', CustomAuthToken.as_view())
+]
+With Django admin
TokenAdmin class to customize it to your needs, more specifically by declaring the user field as raw_field.your_app/admin.py:
@@ -625,6 +639,22 @@ dictionary of items:
raise_exception=True flag, which means that you can override the style of validation error responses globally in your API. To do so, use a custom exception handler, as described above.
+Generic Error Views
+500 Server Error and
+400 Bad Request responses. (Django's default error views provide HTML responses, which may not be appropriate for an
+API-only application.)
+rest_framework.exceptions.server_error500 and application/json content type.handler500:
+handler500 = 'rest_framework.exceptions.server_error'
+
+rest_framework.exceptions.server_error400 and application/json content type.handler400:handler400 = 'rest_framework.exceptions.bad_request'
+
Setting this to False also allows the object attribute or dictionary key to be omitted from output when serializing the instance. If the key is not present it will simply not be included in the output representation.
Defaults to True.
allow_nullNormally an error will be raised if None is passed to a serializer field. Set this keyword argument to True if None should be considered a valid value.
Note that setting this argument to True will imply a default value of null for serialization output, but does not imply a default for input deserialization.
Defaults to False
defaultIf set, this gives the default value that will be used for the field if no input value is supplied. If not set the default behaviour is to not populate the attribute at all.
The default is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned.
May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a set_context method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for validators.
When serializing the instance, default will be used if the the object attribute or dictionary key is not present in the instance.
Note that setting a default value implies that the field is not required. Including both the default and required keyword arguments is invalid and will raise an error.
allow_nullNormally an error will be raised if None is passed to a serializer field. Set this keyword argument to True if None should be considered a valid value.
Note that, without an explicit default, setting this argument to True will imply a default value of null for serialization output, but does not imply a default for input deserialization.
Defaults to False
sourceThe name of the attribute that will be used to populate the field. May be a method that only takes a self argument, such as URLField(source='get_absolute_url'), or may use dotted notation to traverse attributes, such as EmailField(source='user.email'). When serializing fields with dotted notation, it may be necessary to provide a default value if any object is not present or is empty during attribute traversal.
The value source='*' has a special meaning, and is used to indicate that the entire object should be passed through to the field. This can be useful for creating nested representations, or for fields which require access to the complete object in order to determine the output representation.
A preconfigured DictField that is compatible with Django's postgres HStoreField.
Signature: HStoreField(child=<A_FIELD_INSTANCE>)
child - A field instance that is used for validating the values in the dictionary. The default child field accepts both empty strings and null values.Note that the child field must be an instance of CharField, as the hstore extension stores values as strings.
A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON-encoded binary strings.
Signature: JSONField(binary)
The drf-compound-fields package provides "compound" serializer fields, such as lists of simple values, which can be described by other fields rather than serializers with the many=True option. Also provided are fields for typed dictionaries and values that can be either a specific type or a list of items of that type.
The drf-extra-fields package provides extra serializer fields for REST framework, including Base64ImageField and PointField classes.
the djangorestframework-recursive package provides a RecursiveField for serializing and deserializing recursive structures
The django-rest-framework-gis package provides geographic addons for django rest framework like a GeometryField field and a GeoJSON serializer.
If you have specific requirements for creating schema endpoints that are accessed with regular GET requests, you might consider re-using the metadata API for doing so.
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)
diff --git a/api-guide/permissions/index.html b/api-guide/permissions/index.html
index 7da8265bc..f39e4d421 100644
--- a/api-guide/permissions/index.html
+++ b/api-guide/permissions/index.html
@@ -468,6 +468,10 @@
Django Rest Framework API Key
+
+ Django Rest Framework Role Filters
+
+
@@ -680,6 +684,8 @@ class BlacklistPermission(permissions.BasePermission):
The Django Rest Framework Roles package makes it easier to parameterize your API over multiple types of users.
Django Rest Framework API Key
The Django Rest Framework API Key package allows you to ensure that every request made to the server requires an API key header. You can generate one from the django admin interface.
+Django Rest Framework Role Filters
+The Django Rest Framework Role Filters package provides simple filtering over multiple types of roles.
diff --git a/api-guide/routers/index.html b/api-guide/routers/index.html
index 0a6ec01bf..88b0300aa 100644
--- a/api-guide/routers/index.html
+++ b/api-guide/routers/index.html
@@ -519,77 +519,75 @@ urlpatterns += router.urls
url(r'^', include(router.urls)),
]
-Router URL patterns can also be namespaces.
+You may use include with an application namespace:
urlpatterns = [
url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
- url(r'^api/', include(router.urls, namespace='api')),
+ url(r'^api/', include((router.urls, 'app_name'))),
]
-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
-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:
+Or both an application and instance namespace:
+urlpatterns = [
+ url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),
+ url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),
+]
+
+See Django's URL namespaces docs and the include API reference for more details.
+
+Note: 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 examples above you'd need to include a parameter such as
+view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view.
+The automatic view_name generation uses a pattern like %(model_name)-detail. Unless your models names actually clash
+you may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.
+
+Routing for extra actions
+A viewset may mark extra actions for routing by decorating a method with the @action decorator. These extra actions will be included in the generated routes. For example, given the set_password method 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 defaults for either of these values, you can instead provide 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'
+- URL path:
^users/{pk}/change-password/$
+- URL 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.
-For more information see the viewset documentation on marking extra actions for routing.
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 @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.
URL Style HTTP Method Action URL Name
{prefix}/ GET list {basename}-list
POST create
- {prefix}/{methodname}/ GET, or as specified by `methods` argument `@list_route` decorated method {basename}-{methodname}
+ {prefix}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name}
{prefix}/{lookup}/ GET retrieve {basename}-detail
PUT update
PATCH partial_update
DELETE destroy
- {prefix}/{lookup}/{methodname}/ GET, or as specified by `methods` argument `@detail_route` decorated method {basename}-{methodname}
+ {prefix}/{lookup}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name}
By default the URLs created by SimpleRouter are appended with a trailing slash.
@@ -609,12 +607,12 @@ This behavior can be modified by setting the trailing_slash argumen
[.format] GET automatically generated root view api-root
{prefix}/[.format] GET list {basename}-list
POST create
- {prefix}/{methodname}/[.format] GET, or as specified by `methods` argument `@list_route` decorated method {basename}-{methodname}
+ {prefix}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name}
{prefix}/{lookup}/[.format] GET retrieve {basename}-detail
PUT update
PATCH partial_update
DELETE destroy
- {prefix}/{lookup}/{methodname}/[.format] GET, or as specified by `methods` argument `@detail_route` decorated method {basename}-{methodname}
+ {prefix}/{lookup}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name}
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.
@@ -635,17 +633,19 @@ This behavior can be modified by setting the trailing_slash argumen
{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.
-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}.
+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:
+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.
+name: The name of the URL as used in reverse calls. May include the following format strings:
+
+{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.
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, DynamicDetailRoute, SimpleRouter
+from rest_framework.routers import Route, DynamicRoute, SimpleRouter
class CustomReadOnlyRouter(SimpleRouter):
"""
@@ -664,9 +664,10 @@ class CustomReadOnlyRouter(SimpleRouter):
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={}
)
]
@@ -681,7 +682,7 @@ class CustomReadOnlyRouter(SimpleRouter):
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
diff --git a/api-guide/schemas/index.html b/api-guide/schemas/index.html
index c509ac03a..4997d3b88 100644
--- a/api-guide/schemas/index.html
+++ b/api-guide/schemas/index.html
@@ -1014,7 +1014,7 @@ that do not expect a request body.
if method=='POST':
extra_fields = # ... list of extra fields for POST ...
- manual_fields = super().get_manual_fields()
+ manual_fields = super().get_manual_fields(path, method)
return manual_fields + extra_fields
@@ -1044,6 +1044,7 @@ plus an optional description.
The ManualSchema constructor takes two arguments:
fields: A list of coreapi.Field instances. Required.
description: A string description. Optional.
+encoding: Default None. A string encoding, e.g application/json. Optional.
Core API
This documentation gives a brief overview of the components within the coreapi
diff --git a/api-guide/serializers/index.html b/api-guide/serializers/index.html
index b9bf128bf..d99891477 100644
--- a/api-guide/serializers/index.html
+++ b/api-guide/serializers/index.html
@@ -773,7 +773,7 @@ class BlogPostSerializer(serializers.Serializer):
Note: If your <field_name> is declared on your serializer with the parameter required=False then this validation step will not take place if the field is not included.
Object-level validation
-To do any other validation that requires access to multiple fields, add a method called .validate() to your Serializer subclass. This method takes a single argument, which is a dictionary of field values. It should raise a ValidationError if necessary, or just return the validated values. For example:
+To do any other validation that requires access to multiple fields, add a method called .validate() to your Serializer subclass. This method takes a single argument, which is a dictionary of field values. It should raise a serializers.ValidationError if necessary, or just return the validated values. For example:
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
@@ -1306,7 +1306,7 @@ def all_high_scores(request):
return Response(serializer.data)
Read-write BaseSerializer classes
-To create a read-write serializer we first need to implement a .to_internal_value() method. This method returns the validated values that will be used to construct the object instance, and may raise a ValidationError if the supplied data is in an incorrect format.
+To create a read-write serializer we first need to implement a .to_internal_value() method. This method returns the validated values that will be used to construct the object instance, and may raise a serializers.ValidationError if the supplied data is in an incorrect format.
Once you've implemented .to_internal_value(), the basic validation API will be available on the serializer, and you will be able to use .is_valid(), .validated_data and .errors.
If you want to also support .save() you'll need to also implement either or both of the .create() and .update() methods.
Here's a complete example of our previous HighScoreSerializer, that's been updated to support both read and write operations.
@@ -1317,15 +1317,15 @@ def all_high_scores(request):
# Perform the data validation.
if not score:
- raise ValidationError({
+ raise serializers.ValidationError({
'score': 'This field is required.'
})
if not player_name:
- raise ValidationError({
+ raise serializers.ValidationError({
'player_name': 'This field is required.'
})
if len(player_name) > 10:
- raise ValidationError({
+ raise serializers.ValidationError({
'player_name': 'May not be more than 10 characters.'
})
diff --git a/api-guide/settings/index.html b/api-guide/settings/index.html
index 6285af41b..d31189224 100644
--- a/api-guide/settings/index.html
+++ b/api-guide/settings/index.html
@@ -562,7 +562,7 @@ If set to None then generic filtering is disabled.
MAX_PAGINATE_BY
-This setting is pending deprecation.
+This setting has been removed.
See the pagination documentation for further guidance on setting the pagination style.
SEARCH_PARAM
diff --git a/api-guide/throttling/index.html b/api-guide/throttling/index.html
index 8266609af..049b05cea 100644
--- a/api-guide/throttling/index.html
+++ b/api-guide/throttling/index.html
@@ -454,7 +454,7 @@
Throttling
HTTP/1.1 420 Enhance Your Calm
-
+
Throttling is similar to permissions, in that it determines if a request should be authorized. Throttles indicate a temporary state, and are used to control the rate of requests that clients can make to an API.
As with permissions, multiple throttles may be used. Your API might have a restrictive throttle for unauthenticated requests, and a less restrictive throttle for authenticated requests.
diff --git a/api-guide/validators/index.html b/api-guide/validators/index.html
index 439863609..ef18e4be6 100644
--- a/api-guide/validators/index.html
+++ b/api-guide/validators/index.html
@@ -622,7 +622,6 @@ class ExampleSerializer(serializers.Serializer):
A default class that can be used to only set a default argument during create operations. During updates the field is omitted.
It takes a single argument, which is the default value or callable that should be used during create operations.
created_at = serializers.DateTimeField(
- read_only=True,
default=serializers.CreateOnlyDefault(timezone.now)
)
@@ -648,7 +647,7 @@ in the .validate() method, or else in the view.
class Meta:
fields = ('client', 'date', 'amount')
- extra_kwargs = {'client': {'required': 'False'}}
+ extra_kwargs = {'client': {'required': False}}
validators = [] # Remove a default "unique together" constraint.
Updating nested serializers
diff --git a/api-guide/versioning/index.html b/api-guide/versioning/index.html
index d3dcef7a4..4f5cfa9fe 100644
--- a/api-guide/versioning/index.html
+++ b/api-guide/versioning/index.html
@@ -477,7 +477,7 @@ reverse('bookings-list', request=request)
The above function will apply any URL transformations appropriate to the request version. For example:
NamespacedVersioning was being used, and the API version was 'v1', then the URL lookup used would be 'v1:bookings-list', which might resolve to a URL like http://example.org/v1/bookings/.NamespaceVersioning was being used, and the API version was 'v1', then the URL lookup used would be 'v1:bookings-list', which might resolve to a URL like http://example.org/v1/bookings/.QueryParameterVersioning was being used, and the API version was 1.0, then the returned URL might be something like http://example.org/bookings/?version=1.0During 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.
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):
"""
Instantiates and returns the list of permissions that this view requires.
@@ -556,13 +566,11 @@ You may inspect .action to adjust behaviour based on the current ac
return [permission() for permission in permission_classes]
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:
+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.
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
@@ -573,7 +581,7 @@ class UserViewSet(viewsets.ModelViewSet):
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)
@@ -585,7 +593,7 @@ class UserViewSet(viewsets.ModelViewSet):
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')
@@ -597,17 +605,18 @@ class UserViewSet(viewsets.ModelViewSet):
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...
- @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
+The decorator can additionally take extra arguments that will be set for the routed view only. For example:
+ @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:
- @detail_route(methods=['post', 'delete'])
+These decorator will route GET requests by default, but may also accept other HTTP methods by setting the methods argument. For example:
+ @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.
Note that the basename is provided by the router during ViewSet registration. If you are not using a router, then you must provide the basename argument to the .as_view() method.
@@ -616,7 +625,12 @@ class UserViewSet(viewsets.ModelViewSet):
'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.
+>>> 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.
API Reference
ViewSet
diff --git a/css/default.css b/css/default.css
index a0a286b22..bb17a3a11 100644
--- a/css/default.css
+++ b/css/default.css
@@ -160,9 +160,19 @@ body, .navbar .navbar-inner .container-fluid{
margin: 0 auto;
}
-body{
- background: url("../img/grid.png") repeat-x;
- background-attachment: fixed;
+/* Replacement for `body { background-attachment: fixed; }`, which
+ has performance issues when scrolling on large displays. */
+body::before {
+ content: ' ';
+ position: fixed;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ background-color: #f8f8f8;
+ background: url(../img/grid.png) repeat-x;
+ will-change: transform;
+ z-index: -1;
}
diff --git a/mkdocs/search_index.json b/mkdocs/search_index.json
index fbe4210fd..b6345f1b6 100644
--- a/mkdocs/search_index.json
+++ b/mkdocs/search_index.json
@@ -247,7 +247,7 @@
},
{
"location": "/tutorial/4-authentication-and-permissions/",
- "text": "Tutorial 4: Authentication & Permissions\n\n\nCurrently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that:\n\n\n\n\nCode snippets are always associated with a creator.\n\n\nOnly authenticated users may create snippets.\n\n\nOnly the creator of a snippet may update or delete it.\n\n\nUnauthenticated requests should have full read-only access.\n\n\n\n\nAdding information to our model\n\n\nWe're going to make a couple of changes to our \nSnippet\n model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code.\n\n\nAdd the following two fields to the \nSnippet\n model in \nmodels.py\n.\n\n\nowner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField()\n\n\n\nWe'd also need to make sure that when the model is saved, that we populate the highlighted field, using the \npygments\n code highlighting library.\n\n\nWe'll need some extra imports:\n\n\nfrom pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight\n\n\n\nAnd now we can add a \n.save()\n method to our model class:\n\n\ndef save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = self.linenos and 'table' or False\n options = self.title and {'title': self.title} or {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs)\n\n\n\nWhen that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.\n\n\nrm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nYou might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the \ncreatesuperuser\n command.\n\n\npython manage.py createsuperuser\n\n\n\nAdding endpoints for our User models\n\n\nNow that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In \nserializers.py\n add:\n\n\nfrom django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets')\n\n\n\nBecause \n'snippets'\n is a \nreverse\n relationship on the User model, it will not be included by default when using the \nModelSerializer\n class, so we needed to add an explicit field for it.\n\n\nWe'll also add a couple of views to \nviews.py\n. We'd like to just use read-only views for the user representations, so we'll use the \nListAPIView\n and \nRetrieveAPIView\n generic class-based views.\n\n\nfrom django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nMake sure to also import the \nUserSerializer\n class\n\n\nfrom snippets.serializers import UserSerializer\n\n\n\nFinally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in \nurls.py\n.\n\n\nurl(r'^users/$', views.UserList.as_view()),\nurl(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()),\n\n\n\nAssociating Snippets with Users\n\n\nRight now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request.\n\n\nThe way we deal with that is by overriding a \n.perform_create()\n method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL.\n\n\nOn the \nSnippetList\n view class, add the following method:\n\n\ndef perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThe \ncreate()\n method of our serializer will now be passed an additional \n'owner'\n field, along with the validated data from the request.\n\n\nUpdating our serializer\n\n\nNow that snippets are associated with the user that created them, let's update our \nSnippetSerializer\n to reflect that. Add the following field to the serializer definition in \nserializers.py\n:\n\n\nowner = serializers.ReadOnlyField(source='owner.username')\n\n\n\nNote\n: Make sure you also add \n'owner',\n to the list of fields in the inner \nMeta\n class.\n\n\nThis field is doing something quite interesting. The \nsource\n argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language.\n\n\nThe field we've added is the untyped \nReadOnlyField\n class, in contrast to the other typed fields, such as \nCharField\n, \nBooleanField\n etc... The untyped \nReadOnlyField\n is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used \nCharField(read_only=True)\n here.\n\n\nAdding required permissions to views\n\n\nNow that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets.\n\n\nREST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is \nIsAuthenticatedOrReadOnly\n, which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access.\n\n\nFirst add the following import in the views module\n\n\nfrom rest_framework import permissions\n\n\n\nThen, add the following property to \nboth\n the \nSnippetList\n and \nSnippetDetail\n view classes.\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,)\n\n\n\nAdding login to the Browsable API\n\n\nIf you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.\n\n\nWe can add a login view for use with the browsable API, by editing the URLconf in our project-level \nurls.py\n file.\n\n\nAdd the following import at the top of the file:\n\n\nfrom django.conf.urls import include\n\n\n\nAnd, at the end of the file, add a pattern to include the login and logout views for the browsable API.\n\n\nurlpatterns += [\n url(r'^api-auth/', include('rest_framework.urls')),\n]\n\n\n\nThe \nr'^api-auth/'\n part of pattern can actually be whatever URL you want to use.\n\n\nNow if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.\n\n\nOnce you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.\n\n\nObject level permissions\n\n\nReally we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it.\n\n\nTo do that we're going to need to create a custom permission.\n\n\nIn the snippets app, create a new file, \npermissions.py\n\n\nfrom rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user\n\n\n\nNow we can add that custom permission to our snippet instance endpoint, by editing the \npermission_classes\n property on the \nSnippetDetail\n view class:\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n\n\nMake sure to also import the \nIsOwnerOrReadOnly\n class.\n\n\nfrom snippets.permissions import IsOwnerOrReadOnly\n\n\n\nNow, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.\n\n\nAuthenticating with the API\n\n\nBecause we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any \nauthentication classes\n, so the defaults are currently applied, which are \nSessionAuthentication\n and \nBasicAuthentication\n.\n\n\nWhen we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests.\n\n\nIf we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request.\n\n\nIf we try to create a snippet without authenticating, we'll get an error:\n\n\nhttp POST http://127.0.0.1:8000/snippets/ code=\"print 123\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n}\n\n\n\nWe can make a successful request by including the username and password of one of the users we created earlier.\n\n\nhttp -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print 789\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print 789\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSummary\n\n\nWe've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created.\n\n\nIn \npart 5\n of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
+ "text": "Tutorial 4: Authentication & Permissions\n\n\nCurrently our API doesn't have any restrictions on who can edit or delete code snippets. We'd like to have some more advanced behavior in order to make sure that:\n\n\n\n\nCode snippets are always associated with a creator.\n\n\nOnly authenticated users may create snippets.\n\n\nOnly the creator of a snippet may update or delete it.\n\n\nUnauthenticated requests should have full read-only access.\n\n\n\n\nAdding information to our model\n\n\nWe're going to make a couple of changes to our \nSnippet\n model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code.\n\n\nAdd the following two fields to the \nSnippet\n model in \nmodels.py\n.\n\n\nowner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField()\n\n\n\nWe'd also need to make sure that when the model is saved, that we populate the highlighted field, using the \npygments\n code highlighting library.\n\n\nWe'll need some extra imports:\n\n\nfrom pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight\n\n\n\nAnd now we can add a \n.save()\n method to our model class:\n\n\ndef save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs)\n\n\n\nWhen that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again.\n\n\nrm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate\n\n\n\nYou might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the \ncreatesuperuser\n command.\n\n\npython manage.py createsuperuser\n\n\n\nAdding endpoints for our User models\n\n\nNow that we've got some users to work with, we'd better add representations of those users to our API. Creating a new serializer is easy. In \nserializers.py\n add:\n\n\nfrom django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n class Meta:\n model = User\n fields = ('id', 'username', 'snippets')\n\n\n\nBecause \n'snippets'\n is a \nreverse\n relationship on the User model, it will not be included by default when using the \nModelSerializer\n class, so we needed to add an explicit field for it.\n\n\nWe'll also add a couple of views to \nviews.py\n. We'd like to just use read-only views for the user representations, so we'll use the \nListAPIView\n and \nRetrieveAPIView\n generic class-based views.\n\n\nfrom django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nMake sure to also import the \nUserSerializer\n class\n\n\nfrom snippets.serializers import UserSerializer\n\n\n\nFinally we need to add those views into the API, by referencing them from the URL conf. Add the following to the patterns in \nurls.py\n.\n\n\nurl(r'^users/$', views.UserList.as_view()),\nurl(r'^users/(?P[0-9]+)/$', views.UserDetail.as_view()),\n\n\n\nAssociating Snippets with Users\n\n\nRight now, if we created a code snippet, there'd be no way of associating the user that created the snippet, with the snippet instance. The user isn't sent as part of the serialized representation, but is instead a property of the incoming request.\n\n\nThe way we deal with that is by overriding a \n.perform_create()\n method on our snippet views, that allows us to modify how the instance save is managed, and handle any information that is implicit in the incoming request or requested URL.\n\n\nOn the \nSnippetList\n view class, add the following method:\n\n\ndef perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThe \ncreate()\n method of our serializer will now be passed an additional \n'owner'\n field, along with the validated data from the request.\n\n\nUpdating our serializer\n\n\nNow that snippets are associated with the user that created them, let's update our \nSnippetSerializer\n to reflect that. Add the following field to the serializer definition in \nserializers.py\n:\n\n\nowner = serializers.ReadOnlyField(source='owner.username')\n\n\n\nNote\n: Make sure you also add \n'owner',\n to the list of fields in the inner \nMeta\n class.\n\n\nThis field is doing something quite interesting. The \nsource\n argument controls which attribute is used to populate a field, and can point at any attribute on the serialized instance. It can also take the dotted notation shown above, in which case it will traverse the given attributes, in a similar way as it is used with Django's template language.\n\n\nThe field we've added is the untyped \nReadOnlyField\n class, in contrast to the other typed fields, such as \nCharField\n, \nBooleanField\n etc... The untyped \nReadOnlyField\n is always read-only, and will be used for serialized representations, but will not be used for updating model instances when they are deserialized. We could have also used \nCharField(read_only=True)\n here.\n\n\nAdding required permissions to views\n\n\nNow that code snippets are associated with users, we want to make sure that only authenticated users are able to create, update and delete code snippets.\n\n\nREST framework includes a number of permission classes that we can use to restrict who can access a given view. In this case the one we're looking for is \nIsAuthenticatedOrReadOnly\n, which will ensure that authenticated requests get read-write access, and unauthenticated requests get read-only access.\n\n\nFirst add the following import in the views module\n\n\nfrom rest_framework import permissions\n\n\n\nThen, add the following property to \nboth\n the \nSnippetList\n and \nSnippetDetail\n view classes.\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,)\n\n\n\nAdding login to the Browsable API\n\n\nIf you open a browser and navigate to the browsable API at the moment, you'll find that you're no longer able to create new code snippets. In order to do so we'd need to be able to login as a user.\n\n\nWe can add a login view for use with the browsable API, by editing the URLconf in our project-level \nurls.py\n file.\n\n\nAdd the following import at the top of the file:\n\n\nfrom django.conf.urls import include\n\n\n\nAnd, at the end of the file, add a pattern to include the login and logout views for the browsable API.\n\n\nurlpatterns += [\n url(r'^api-auth/', include('rest_framework.urls')),\n]\n\n\n\nThe \nr'^api-auth/'\n part of pattern can actually be whatever URL you want to use.\n\n\nNow if you open up the browser again and refresh the page you'll see a 'Login' link in the top right of the page. If you log in as one of the users you created earlier, you'll be able to create code snippets again.\n\n\nOnce you've created a few code snippets, navigate to the '/users/' endpoint, and notice that the representation includes a list of the snippet ids that are associated with each user, in each user's 'snippets' field.\n\n\nObject level permissions\n\n\nReally we'd like all code snippets to be visible to anyone, but also make sure that only the user that created a code snippet is able to update or delete it.\n\n\nTo do that we're going to need to create a custom permission.\n\n\nIn the snippets app, create a new file, \npermissions.py\n\n\nfrom rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n \"\"\"\n Custom permission to only allow owners of an object to edit it.\n \"\"\"\n\n def has_object_permission(self, request, view, obj):\n # Read permissions are allowed to any request,\n # so we'll always allow GET, HEAD or OPTIONS requests.\n if request.method in permissions.SAFE_METHODS:\n return True\n\n # Write permissions are only allowed to the owner of the snippet.\n return obj.owner == request.user\n\n\n\nNow we can add that custom permission to our snippet instance endpoint, by editing the \npermission_classes\n property on the \nSnippetDetail\n view class:\n\n\npermission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n\n\nMake sure to also import the \nIsOwnerOrReadOnly\n class.\n\n\nfrom snippets.permissions import IsOwnerOrReadOnly\n\n\n\nNow, if you open a browser again, you find that the 'DELETE' and 'PUT' actions only appear on a snippet instance endpoint if you're logged in as the same user that created the code snippet.\n\n\nAuthenticating with the API\n\n\nBecause we now have a set of permissions on the API, we need to authenticate our requests to it if we want to edit any snippets. We haven't set up any \nauthentication classes\n, so the defaults are currently applied, which are \nSessionAuthentication\n and \nBasicAuthentication\n.\n\n\nWhen we interact with the API through the web browser, we can login, and the browser session will then provide the required authentication for the requests.\n\n\nIf we're interacting with the API programmatically we need to explicitly provide the authentication credentials on each request.\n\n\nIf we try to create a snippet without authenticating, we'll get an error:\n\n\nhttp POST http://127.0.0.1:8000/snippets/ code=\"print 123\"\n\n{\n \"detail\": \"Authentication credentials were not provided.\"\n}\n\n\n\nWe can make a successful request by including the username and password of one of the users we created earlier.\n\n\nhttp -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print 789\"\n\n{\n \"id\": 1,\n \"owner\": \"admin\",\n \"title\": \"foo\",\n \"code\": \"print 789\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nSummary\n\n\nWe've now got a fairly fine-grained set of permissions on our Web API, and end points for users of the system and for the code snippets that they have created.\n\n\nIn \npart 5\n of the tutorial we'll look at how we can tie everything together by creating an HTML endpoint for our highlighted snippets, and improve the cohesion of our API by using hyperlinking for the relationships within the system.",
"title": "4 - Authentication and permissions"
},
{
@@ -257,7 +257,7 @@
},
{
"location": "/tutorial/4-authentication-and-permissions/#adding-information-to-our-model",
- "text": "We're going to make a couple of changes to our Snippet model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code. Add the following two fields to the Snippet model in models.py . owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField() We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the pygments code highlighting library. We'll need some extra imports: from pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight And now we can add a .save() method to our model class: def save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = self.linenos and 'table' or False\n options = self.title and {'title': self.title} or {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs) When that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. rm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate You might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the createsuperuser command. python manage.py createsuperuser",
+ "text": "We're going to make a couple of changes to our Snippet model class.\nFirst, let's add a couple of fields. One of those fields will be used to represent the user who created the code snippet. The other field will be used to store the highlighted HTML representation of the code. Add the following two fields to the Snippet model in models.py . owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField() We'd also need to make sure that when the model is saved, that we populate the highlighted field, using the pygments code highlighting library. We'll need some extra imports: from pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight And now we can add a .save() method to our model class: def save(self, *args, **kwargs):\n \"\"\"\n Use the `pygments` library to create a highlighted HTML\n representation of the code snippet.\n \"\"\"\n lexer = get_lexer_by_name(self.language)\n linenos = 'table' if self.linenos else False\n options = {'title': self.title} if self.title else {}\n formatter = HtmlFormatter(style=self.style, linenos=linenos,\n full=True, **options)\n self.highlighted = highlight(self.code, lexer, formatter)\n super(Snippet, self).save(*args, **kwargs) When that's all done we'll need to update our database tables.\nNormally we'd create a database migration in order to do that, but for the purposes of this tutorial, let's just delete the database and start again. rm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate You might also want to create a few different users, to use for testing the API. The quickest way to do this will be with the createsuperuser command. python manage.py createsuperuser",
"title": "Adding information to our model"
},
{
@@ -342,7 +342,7 @@
},
{
"location": "/tutorial/6-viewsets-and-routers/",
- "text": "Tutorial 6: ViewSets & Routers\n\n\nREST framework includes an abstraction for dealing with \nViewSets\n, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.\n\n\nViewSet\n classes are almost the same thing as \nView\n classes, except that they provide operations such as \nread\n, or \nupdate\n, and not method handlers such as \nget\n or \nput\n.\n\n\nA \nViewSet\n class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a \nRouter\n class which handles the complexities of defining the URL conf for you.\n\n\nRefactoring to use ViewSets\n\n\nLet's take our current set of views, and refactor them into view sets.\n\n\nFirst of all let's refactor our \nUserList\n and \nUserDetail\n views into a single \nUserViewSet\n. We can remove the two views, and replace them with a single class:\n\n\nfrom rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nHere we've used the \nReadOnlyModelViewSet\n class to automatically provide the default 'read-only' operations. We're still setting the \nqueryset\n and \nserializer_class\n attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.\n\n\nNext we're going to replace the \nSnippetList\n, \nSnippetDetail\n and \nSnippetHighlight\n view classes. We can remove the three views, and again replace them with a single class.\n\n\nfrom rest_framework.decorators import detail_route\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThis time we've used the \nModelViewSet\n class in order to get the complete set of default read and write operations.\n\n\nNotice that we've also used the \n@detail_route\n decorator to create a custom action, named \nhighlight\n. This decorator can be used to add any custom endpoints that don't fit into the standard \ncreate\n/\nupdate\n/\ndelete\n style.\n\n\nCustom actions which use the \n@detail_route\n decorator will respond to \nGET\n requests by default. We can use the \nmethods\n argument if we wanted an action that responded to \nPOST\n requests.\n\n\nThe 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.\n\n\nBinding ViewSets to URLs explicitly\n\n\nThe handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets.\n\n\nIn the \nsnippets/urls.py\n file we bind our \nViewSet\n classes into a set of concrete views.\n\n\nfrom snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n})\n\n\n\nNotice how we're creating multiple views from each \nViewSet\n class, by binding the http methods to the required action for each view.\n\n\nNow that we've bound our resources into concrete views, we can register the views with the URL conf as usual.\n\n\nurlpatterns = format_suffix_patterns([\n url(r'^$', api_root),\n url(r'^snippets/$', snippet_list, name='snippet-list'),\n url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'),\n url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),\n url(r'^users/$', user_list, name='user-list'),\n url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail')\n])\n\n\n\nUsing Routers\n\n\nBecause we're using \nViewSet\n classes rather than \nView\n classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a \nRouter\n class. All we need to do is register the appropriate view sets with a router, and let it do the rest.\n\n\nHere's our re-wired \nsnippets/urls.py\n file.\n\n\nfrom django.conf.urls import url, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n url(r'^', include(router.urls))\n]\n\n\n\nRegistering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.\n\n\nThe \nDefaultRouter\n class we're using also automatically creates the API root view for us, so we can now delete the \napi_root\n method from our \nviews\n module.\n\n\nTrade-offs between views vs viewsets\n\n\nUsing viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.\n\n\nThat doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.\n\n\nIn \npart 7\n of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
+ "text": "Tutorial 6: ViewSets & Routers\n\n\nREST framework includes an abstraction for dealing with \nViewSets\n, that allows the developer to concentrate on modeling the state and interactions of the API, and leave the URL construction to be handled automatically, based on common conventions.\n\n\nViewSet\n classes are almost the same thing as \nView\n classes, except that they provide operations such as \nread\n, or \nupdate\n, and not method handlers such as \nget\n or \nput\n.\n\n\nA \nViewSet\n class is only bound to a set of method handlers at the last moment, when it is instantiated into a set of views, typically by using a \nRouter\n class which handles the complexities of defining the URL conf for you.\n\n\nRefactoring to use ViewSets\n\n\nLet's take our current set of views, and refactor them into view sets.\n\n\nFirst of all let's refactor our \nUserList\n and \nUserDetail\n views into a single \nUserViewSet\n. We can remove the two views, and replace them with a single class:\n\n\nfrom rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n\n\nHere we've used the \nReadOnlyModelViewSet\n class to automatically provide the default 'read-only' operations. We're still setting the \nqueryset\n and \nserializer_class\n attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.\n\n\nNext we're going to replace the \nSnippetList\n, \nSnippetDetail\n and \nSnippetHighlight\n view classes. We can remove the three views, and again replace them with a single class.\n\n\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user)\n\n\n\nThis time we've used the \nModelViewSet\n class in order to get the complete set of default read and write operations.\n\n\nNotice that we've also used the \n@action\n decorator to create a custom action, named \nhighlight\n. This decorator can be used to add any custom endpoints that don't fit into the standard \ncreate\n/\nupdate\n/\ndelete\n style.\n\n\nCustom actions which use the \n@action\n decorator will respond to \nGET\n requests by default. We can use the \nmethods\n argument if we wanted an action that responded to \nPOST\n requests.\n\n\nThe 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 \nurl_path\n as a decorator keyword argument.\n\n\nBinding ViewSets to URLs explicitly\n\n\nThe handler methods only get bound to the actions when we define the URLConf.\nTo see what's going on under the hood let's first explicitly create a set of views from our ViewSets.\n\n\nIn the \nsnippets/urls.py\n file we bind our \nViewSet\n classes into a set of concrete views.\n\n\nfrom snippets.views import SnippetViewSet, UserViewSet, api_root\nfrom rest_framework import renderers\n\nsnippet_list = SnippetViewSet.as_view({\n 'get': 'list',\n 'post': 'create'\n})\nsnippet_detail = SnippetViewSet.as_view({\n 'get': 'retrieve',\n 'put': 'update',\n 'patch': 'partial_update',\n 'delete': 'destroy'\n})\nsnippet_highlight = SnippetViewSet.as_view({\n 'get': 'highlight'\n}, renderer_classes=[renderers.StaticHTMLRenderer])\nuser_list = UserViewSet.as_view({\n 'get': 'list'\n})\nuser_detail = UserViewSet.as_view({\n 'get': 'retrieve'\n})\n\n\n\nNotice how we're creating multiple views from each \nViewSet\n class, by binding the http methods to the required action for each view.\n\n\nNow that we've bound our resources into concrete views, we can register the views with the URL conf as usual.\n\n\nurlpatterns = format_suffix_patterns([\n url(r'^$', api_root),\n url(r'^snippets/$', snippet_list, name='snippet-list'),\n url(r'^snippets/(?P[0-9]+)/$', snippet_detail, name='snippet-detail'),\n url(r'^snippets/(?P[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),\n url(r'^users/$', user_list, name='user-list'),\n url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail')\n])\n\n\n\nUsing Routers\n\n\nBecause we're using \nViewSet\n classes rather than \nView\n classes, we actually don't need to design the URL conf ourselves. The conventions for wiring up resources into views and urls can be handled automatically, using a \nRouter\n class. All we need to do is register the appropriate view sets with a router, and let it do the rest.\n\n\nHere's our re-wired \nsnippets/urls.py\n file.\n\n\nfrom django.conf.urls import url, include\nfrom rest_framework.routers import DefaultRouter\nfrom snippets import views\n\n# Create a router and register our viewsets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet)\nrouter.register(r'users', views.UserViewSet)\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n url(r'^', include(router.urls))\n]\n\n\n\nRegistering the viewsets with the router is similar to providing a urlpattern. We include two arguments - the URL prefix for the views, and the viewset itself.\n\n\nThe \nDefaultRouter\n class we're using also automatically creates the API root view for us, so we can now delete the \napi_root\n method from our \nviews\n module.\n\n\nTrade-offs between views vs viewsets\n\n\nUsing viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.\n\n\nThat doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.\n\n\nIn \npart 7\n of the tutorial we'll look at how we can add an API schema,\nand interact with our API using a client library or command line tool.",
"title": "6 - Viewsets and routers"
},
{
@@ -352,7 +352,7 @@
},
{
"location": "/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets",
- "text": "Let's take our current set of views, and refactor them into view sets. First of all let's refactor our UserList and UserDetail views into a single UserViewSet . We can remove the two views, and replace them with a single class: from rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer Here we've used the ReadOnlyModelViewSet class to automatically provide the default 'read-only' operations. We're still setting the queryset and serializer_class attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes. 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\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user) 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. 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. 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.",
+ "text": "Let's take our current set of views, and refactor them into view sets. First of all let's refactor our UserList and UserDetail views into a single UserViewSet . We can remove the two views, and replace them with a single class: from rest_framework import viewsets\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n This viewset automatically provides `list` and `detail` actions.\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer Here we've used the ReadOnlyModelViewSet class to automatically provide the default 'read-only' operations. We're still setting the queryset and serializer_class attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes. 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 action\nfrom rest_framework.response import Response\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n \"\"\"\n This viewset automatically provides `list`, `create`, `retrieve`,\n `update` and `destroy` actions.\n\n Additionally we also provide an extra `highlight` action.\n \"\"\"\n queryset = Snippet.objects.all()\n serializer_class = SnippetSerializer\n permission_classes = (permissions.IsAuthenticatedOrReadOnly,\n IsOwnerOrReadOnly,)\n\n @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n def highlight(self, request, *args, **kwargs):\n snippet = self.get_object()\n return Response(snippet.highlighted)\n\n def perform_create(self, serializer):\n serializer.save(owner=self.request.user) 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 @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 @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.",
"title": "Refactoring to use ViewSets"
},
{
@@ -372,7 +372,7 @@
},
{
"location": "/tutorial/7-schemas-and-client-libraries/",
- "text": "Tutorial 7: Schemas & client libraries\n\n\nA schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support.\n\n\nSchemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.\n\n\nCore API\n\n\nIn order to provide schema support REST framework uses \nCore API\n.\n\n\nCore API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side.\n\n\nWhen used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats.\n\n\nWhen used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.\n\n\nAdding a schema\n\n\nREST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation.\n\n\nYou'll need to install the \ncoreapi\n python package in order to include an\nAPI schema.\n\n\n$ pip install coreapi\n\n\n\nWe can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration.\n\n\nfrom rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0url(r'^schema/$', schema_view),\n ...\n]\n\n\n\n\nIf you visit the API root endpoint in a browser you should now see \ncorejson\n\nrepresentation become available as an option.\n\n\n\n\nWe can also request the schema from the command line, by specifying the desired\ncontent type in the \nAccept\n header.\n\n\n$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ...\n\n\n\nThe default output style is to use the \nCore JSON\n encoding.\n\n\nOther schema formats, such as \nOpen API\n (formerly Swagger) are\nalso supported.\n\n\nUsing a command line client\n\n\nNow that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client.\n\n\nThe command line client is available as the \ncoreapi-cli\n package:\n\n\n$ pip install coreapi-cli\n\n\n\nNow check that it is available on the command line...\n\n\n$ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit http://www.coreapi.org for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n...\n\n\n\nFirst we'll load the API schema using the command line client.\n\n\n$ coreapi get http://127.0.0.1:8000/schema/\n\n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API.\n\n\nLet's try listing the existing snippets, using the command line client:\n\n\n$ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ...\n\n\n\nSome of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id.\n\n\n$ coreapi action snippets highlight --param id=1\n\n\n\n\n Example \n ...\n\n\n\nAuthenticating our client\n\n\nIf we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth.\n\n\nMake sure to replace the \n\n and \n\n below with your\nactual username and password.\n\n\n$ coreapi credentials add 127.0.0.1 : --auth basic\nAdded credentials\n127.0.0.1 \"Basic <...>\"\n\n\n\nNow if we fetch the schema again, we should be able to see the full\nset of available interactions.\n\n\n$ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\">\n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe're now able to interact with these endpoints. For example, to create a new\nsnippet:\n\n\n$ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nAnd to delete a snippet:\n\n\n$ coreapi action snippets delete --param id=7\n\n\n\nAs well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon.\n\n\nFor more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.\n\n\nReviewing our work\n\n\nWith an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats.\n\n\nWe've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.\n\n\nYou can review the final \ntutorial code\n on GitHub, or try out a live example in \nthe sandbox\n.\n\n\nOnwards and upwards\n\n\nWe've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:\n\n\n\n\nContribute on \nGitHub\n by reviewing and submitting issues, and making pull requests.\n\n\nJoin the \nREST framework discussion group\n, and help build the community.\n\n\nFollow \nthe author\n on Twitter and say hi.\n\n\n\n\nNow go build awesome things.",
+ "text": "Tutorial 7: Schemas & client libraries\n\n\nA schema is a machine-readable document that describes the available API\nendpoints, their URLS, and what operations they support.\n\n\nSchemas can be a useful tool for auto-generated documentation, and can also\nbe used to drive dynamic client libraries that can interact with the API.\n\n\nCore API\n\n\nIn order to provide schema support REST framework uses \nCore API\n.\n\n\nCore API is a document specification for describing APIs. It is used to provide\nan internal representation format of the available endpoints and possible\ninteractions that an API exposes. It can either be used server-side, or\nclient-side.\n\n\nWhen used server-side, Core API allows an API to support rendering to a wide\nrange of schema or hypermedia formats.\n\n\nWhen used client-side, Core API allows for dynamically driven client libraries\nthat can interact with any API that exposes a supported schema or hypermedia\nformat.\n\n\nAdding a schema\n\n\nREST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation.\n\n\nYou'll need to install the \ncoreapi\n python package in order to include an\nAPI schema.\n\n\n$ pip install coreapi\n\n\n\nWe can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration.\n\n\nfrom rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0url(r'^schema/$', schema_view),\n ...\n]\n\n\n\n\nIf you visit the \n/schema/\n endpoint in a browser you should now see \ncorejson\n\nrepresentation become available as an option.\n\n\n\n\nWe can also request the schema from the command line, by specifying the desired\ncontent type in the \nAccept\n header.\n\n\n$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ...\n\n\n\nThe default output style is to use the \nCore JSON\n encoding.\n\n\nOther schema formats, such as \nOpen API\n (formerly Swagger) are\nalso supported.\n\n\nUsing a command line client\n\n\nNow that our API is exposing a schema endpoint, we can use a dynamic client\nlibrary to interact with the API. To demonstrate this, let's use the\nCore API command line client.\n\n\nThe command line client is available as the \ncoreapi-cli\n package:\n\n\n$ pip install coreapi-cli\n\n\n\nNow check that it is available on the command line...\n\n\n$ coreapi\nUsage: coreapi [OPTIONS] COMMAND [ARGS]...\n\n Command line client for interacting with CoreAPI services.\n\n Visit http://www.coreapi.org for more information.\n\nOptions:\n --version Display the package version number.\n --help Show this message and exit.\n\nCommands:\n...\n\n\n\nFirst we'll load the API schema using the command line client.\n\n\n$ coreapi get http://127.0.0.1:8000/schema/\n\n snippets: {\n highlight(id)\n list()\n read(id)\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe haven't authenticated yet, so right now we're only able to see the read only\nendpoints, in line with how we've set up the permissions on the API.\n\n\nLet's try listing the existing snippets, using the command line client:\n\n\n$ coreapi action snippets list\n[\n {\n \"url\": \"http://127.0.0.1:8000/snippets/1/\",\n \"id\": 1,\n \"highlight\": \"http://127.0.0.1:8000/snippets/1/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world!')\",\n \"linenos\": true,\n \"language\": \"python\",\n \"style\": \"friendly\"\n },\n ...\n\n\n\nSome of the API endpoints require named parameters. For example, to get back\nthe highlight HTML for a particular snippet we need to provide an id.\n\n\n$ coreapi action snippets highlight --param id=1\n\n\n\n\n Example \n ...\n\n\n\nAuthenticating our client\n\n\nIf we want to be able to create, edit and delete snippets, we'll need to\nauthenticate as a valid user. In this case we'll just use basic auth.\n\n\nMake sure to replace the \n\n and \n\n below with your\nactual username and password.\n\n\n$ coreapi credentials add 127.0.0.1 : --auth basic\nAdded credentials\n127.0.0.1 \"Basic <...>\"\n\n\n\nNow if we fetch the schema again, we should be able to see the full\nset of available interactions.\n\n\n$ coreapi reload\nPastebin API \"http://127.0.0.1:8000/schema/\">\n snippets: {\n create(code, [title], [linenos], [language], [style])\n delete(id)\n highlight(id)\n list()\n partial_update(id, [title], [code], [linenos], [language], [style])\n read(id)\n update(id, code, [title], [linenos], [language], [style])\n }\n users: {\n list()\n read(id)\n }\n\n\n\nWe're now able to interact with these endpoints. For example, to create a new\nsnippet:\n\n\n$ coreapi action snippets create --param title=\"Example\" --param code=\"print('hello, world')\"\n{\n \"url\": \"http://127.0.0.1:8000/snippets/7/\",\n \"id\": 7,\n \"highlight\": \"http://127.0.0.1:8000/snippets/7/highlight/\",\n \"owner\": \"lucy\",\n \"title\": \"Example\",\n \"code\": \"print('hello, world')\",\n \"linenos\": false,\n \"language\": \"python\",\n \"style\": \"friendly\"\n}\n\n\n\nAnd to delete a snippet:\n\n\n$ coreapi action snippets delete --param id=7\n\n\n\nAs well as the command line client, developers can also interact with your\nAPI using client libraries. The Python client library is the first of these\nto be available, and a Javascript client library is planned to be released\nsoon.\n\n\nFor more details on customizing schema generation and using Core API\nclient libraries you'll need to refer to the full documentation.\n\n\nReviewing our work\n\n\nWith an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, includes a schema-driven client library, and comes complete with authentication, per-object permissions, and multiple renderer formats.\n\n\nWe've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.\n\n\nYou can review the final \ntutorial code\n on GitHub, or try out a live example in \nthe sandbox\n.\n\n\nOnwards and upwards\n\n\nWe've reached the end of our tutorial. If you want to get more involved in the REST framework project, here are a few places you can start:\n\n\n\n\nContribute on \nGitHub\n by reviewing and submitting issues, and making pull requests.\n\n\nJoin the \nREST framework discussion group\n, and help build the community.\n\n\nFollow \nthe author\n on Twitter and say hi.\n\n\n\n\nNow go build awesome things.",
"title": "7 - Schemas and client libraries"
},
{
@@ -387,7 +387,7 @@
},
{
"location": "/tutorial/7-schemas-and-client-libraries/#adding-a-schema",
- "text": "REST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation. You'll need to install the coreapi python package in order to include an\nAPI schema. $ pip install coreapi We can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration. from rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0url(r'^schema/$', schema_view),\n ...\n] If you visit the API root endpoint in a browser you should now see corejson \nrepresentation become available as an option. We can also request the schema from the command line, by specifying the desired\ncontent type in the Accept header. $ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ... The default output style is to use the Core JSON encoding. Other schema formats, such as Open API (formerly Swagger) are\nalso supported.",
+ "text": "REST framework supports either explicitly defined schema views, or\nautomatically generated schemas. Since we're using viewsets and routers,\nwe can simply use the automatic schema generation. You'll need to install the coreapi python package in order to include an\nAPI schema. $ pip install coreapi We can now include a schema for our API, by including an autogenerated schema\nview in our URL configuration. from rest_framework.schemas import get_schema_view\n\nschema_view = get_schema_view(title='Pastebin API')\n\nurlpatterns = [\n \u00a0 \u00a0url(r'^schema/$', schema_view),\n ...\n] If you visit the /schema/ endpoint in a browser you should now see corejson \nrepresentation become available as an option. We can also request the schema from the command line, by specifying the desired\ncontent type in the Accept header. $ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json\nHTTP/1.0 200 OK\nAllow: GET, HEAD, OPTIONS\nContent-Type: application/coreapi+json\n\n{\n \"_meta\": {\n \"title\": \"Pastebin API\"\n },\n \"_type\": \"document\",\n ... The default output style is to use the Core JSON encoding. Other schema formats, such as Open API (formerly Swagger) are\nalso supported.",
"title": "Adding a schema"
},
{
@@ -892,7 +892,7 @@
},
{
"location": "/api-guide/viewsets/",
- "text": "ViewSets\n\n\n\n\nAfter routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nDjango REST framework allows you to combine the logic for a set of related views in a single class, called a \nViewSet\n. In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.\n\n\nA \nViewSet\n class is simply \na type of class-based View, that does not provide any method handlers\n such as \n.get()\n or \n.post()\n, and instead provides actions such as \n.list()\n and \n.create()\n.\n\n\nThe method handlers for a \nViewSet\n are only bound to the corresponding actions at the point of finalizing the view, using the \n.as_view()\n method.\n\n\nTypically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.\n\n\nExample\n\n\nLet's define a simple viewset that can be used to list or retrieve all the users in the system.\n\n\nfrom django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data)\n\n\n\nIf we need to, we can bind this viewset into two separate views, like so:\n\n\nuser_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'})\n\n\n\nTypically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.\n\n\nfrom myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, base_name='user')\nurlpatterns = router.urls\n\n\n\nRather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all()\n\n\n\nThere are two main advantages of using a \nViewSet\n class over using a \nView\n class.\n\n\n\n\nRepeated logic can be combined into a single class. In the above example, we only need to specify the \nqueryset\n once, and it'll be used across multiple views.\n\n\nBy using routers, we no longer need to deal with wiring up the URL conf ourselves.\n\n\n\n\nBoth 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.\n\n\nViewSet actions\n\n\nThe default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below:\n\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass\n\n\n\nDuring dispatch the name of the current action is available via the \n.action\n attribute.\nYou may inspect \n.action\n to adjust behaviour based on the current action.\n\n\nFor example, you could restrict permissions to everything except the \nlist\n action similar to this:\n\n\ndef get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]\n\n\n\nMarking extra actions for routing\n\n\nIf you have ad-hoc methods that you need to be routed to, you can mark them as requiring routing using the \n@detail_route\n or \n@list_route\n decorators.\n\n\nThe \n@detail_route\n decorator contains \npk\n in its URL pattern and is intended for methods which require a single instance. The \n@list_route\n decorator is intended for methods which operate on a list of objects.\n\n\nFor example:\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework import status\nfrom rest_framework import viewsets\nfrom rest_framework.decorators import detail_route, list_route\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @detail_route(methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @list_route()\n def recent_users(self, request):\n recent_users = User.objects.all().order('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data)\n\n\n\nThe decorators can additionally take extra arguments that will be set for the routed view only. For example...\n\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThese decorators will route \nGET\n requests by default, but may also accept other HTTP methods, by using the \nmethods\n argument. For example:\n\n\n @detail_route(methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ...\n\n\n\nThe two new actions will then be available at the urls \n^users/{pk}/set_password/$\n and \n^users/{pk}/unset_password/$\n\n\nReversing action URLs\n\n\nIf you need to get the URL of an action, use the \n.reverse_action()\n method. This is a convenience wrapper for \nreverse()\n, automatically passing the view's \nrequest\n object and prepending the \nurl_name\n with the \n.basename\n attribute.\n\n\nNote that the \nbasename\n is provided by the router during \nViewSet\n registration. If you are not using a router, then you must provide the \nbasename\n argument to the \n.as_view()\n method.\n\n\nUsing the example from the previous section:\n\n\n>>> view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nThe \nurl_name\n argument should match the same argument to the \n@list_route\n and \n@detail_route\n decorators. Additionally, this can be used to reverse the default \nlist\n and \ndetail\n routes.\n\n\n\n\nAPI Reference\n\n\nViewSet\n\n\nThe \nViewSet\n class inherits from \nAPIView\n. You can use any of the standard attributes such as \npermission_classes\n, \nauthentication_classes\n in order to control the API policy on the viewset.\n\n\nThe \nViewSet\n class does not provide any implementations of actions. In order to use a \nViewSet\n class you'll override the class and define the action implementations explicitly.\n\n\nGenericViewSet\n\n\nThe \nGenericViewSet\n class inherits from \nGenericAPIView\n, and provides the default set of \nget_object\n, \nget_queryset\n methods and other generic view base behavior, but does not include any actions by default.\n\n\nIn order to use a \nGenericViewSet\n class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.\n\n\nModelViewSet\n\n\nThe \nModelViewSet\n class inherits from \nGenericAPIView\n and includes implementations for various actions, by mixing in the behavior of the various mixin classes.\n\n\nThe actions provided by the \nModelViewSet\n class are \n.list()\n, \n.retrieve()\n, \n.create()\n, \n.update()\n, \n.partial_update()\n, and \n.destroy()\n.\n\n\nExample\n\n\nBecause \nModelViewSet\n extends \nGenericAPIView\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n\n\nNote that you can use any of the standard attributes or method overrides provided by \nGenericAPIView\n. For example, to use a \nViewSet\n that dynamically determines the queryset it should operate on, you might do something like this:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all()\n\n\n\nNote however that upon removal of the \nqueryset\n property from your \nViewSet\n, any associated \nrouter\n will be unable to derive the base_name of your Model automatically, and so you will have to specify the \nbase_name\n kwarg as part of your \nrouter registration\n.\n\n\nAlso note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.\n\n\nReadOnlyModelViewSet\n\n\nThe \nReadOnlyModelViewSet\n class also inherits from \nGenericAPIView\n. As with \nModelViewSet\n it also includes implementations for various actions, but unlike \nModelViewSet\n only provides the 'read-only' actions, \n.list()\n and \n.retrieve()\n.\n\n\nExample\n\n\nAs with \nModelViewSet\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n\n\n\nAgain, as with \nModelViewSet\n, you can use any of the standard attributes and method overrides available to \nGenericAPIView\n.\n\n\nCustom ViewSet base classes\n\n\nYou may need to provide custom \nViewSet\n classes that do not have the full set of \nModelViewSet\n actions, or that customize the behavior in some other way.\n\n\nExample\n\n\nTo create a base viewset class that provides \ncreate\n, \nlist\n and \nretrieve\n operations, inherit from \nGenericViewSet\n, and mixin the required actions:\n\n\nfrom rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass\n\n\n\nBy creating your own base \nViewSet\n classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
+ "text": "ViewSets\n\n\n\n\nAfter routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nDjango REST framework allows you to combine the logic for a set of related views in a single class, called a \nViewSet\n. In other frameworks you may also find conceptually similar implementations named something like 'Resources' or 'Controllers'.\n\n\nA \nViewSet\n class is simply \na type of class-based View, that does not provide any method handlers\n such as \n.get()\n or \n.post()\n, and instead provides actions such as \n.list()\n and \n.create()\n.\n\n\nThe method handlers for a \nViewSet\n are only bound to the corresponding actions at the point of finalizing the view, using the \n.as_view()\n method.\n\n\nTypically, rather than explicitly registering the views in a viewset in the urlconf, you'll register the viewset with a router class, that automatically determines the urlconf for you.\n\n\nExample\n\n\nLet's define a simple viewset that can be used to list or retrieve all the users in the system.\n\n\nfrom django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n A simple ViewSet for listing or retrieving users.\n \"\"\"\n def list(self, request):\n queryset = User.objects.all()\n serializer = UserSerializer(queryset, many=True)\n return Response(serializer.data)\n\n def retrieve(self, request, pk=None):\n queryset = User.objects.all()\n user = get_object_or_404(queryset, pk=pk)\n serializer = UserSerializer(user)\n return Response(serializer.data)\n\n\n\nIf we need to, we can bind this viewset into two separate views, like so:\n\n\nuser_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'})\n\n\n\nTypically we wouldn't do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.\n\n\nfrom myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, base_name='user')\nurlpatterns = router.urls\n\n\n\nRather than writing your own viewsets, you'll often want to use the existing base classes that provide a default set of behavior. For example:\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset for viewing and editing user instances.\n \"\"\"\n serializer_class = UserSerializer\n queryset = User.objects.all()\n\n\n\nThere are two main advantages of using a \nViewSet\n class over using a \nView\n class.\n\n\n\n\nRepeated logic can be combined into a single class. In the above example, we only need to specify the \nqueryset\n once, and it'll be used across multiple views.\n\n\nBy using routers, we no longer need to deal with wiring up the URL conf ourselves.\n\n\n\n\nBoth 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.\n\n\nViewSet actions\n\n\nThe default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below:\n\n\nclass UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass\n\n\n\nIntrospecting ViewSet actions\n\n\nDuring dispatch, the following attributes are available on the \nViewSet\n.\n\n\n\n\nbasename\n - the base to use for the URL names that are created.\n\n\naction\n - the name of the current action (e.g., \nlist\n, \ncreate\n).\n\n\ndetail\n - boolean indicating if the current action is configured for a list or detail view.\n\n\nsuffix\n - the display suffix for the viewset type - mirrors the \ndetail\n attribute.\n\n\n\n\nYou may inspect these attributes to adjust behaviour based on the current action. For example, you could restrict permissions to everything except the \nlist\n action similar to this:\n\n\ndef get_permissions(self):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]\n\n\n\nMarking extra actions for routing\n\n\nIf you have ad-hoc methods that should be routable, you can mark them as such with the \n@action\n decorator. Like regular actions, extra actions may be intended for either a list of objects, or a single instance. To indicate this, set the \ndetail\n argument to \nTrue\n or \nFalse\n. The router will configure its URL patterns accordingly. e.g., the \nDefaultRouter\n will configure detail actions to contain \npk\n in their URL patterns.\n\n\nA more complete example of extra actions:\n\n\nfrom django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(methods=['post'], detail=True)\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data)\n\n\n\nThe decorator can additionally take extra arguments that will be set for the routed view only. For example:\n\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThese decorator will route \nGET\n requests by default, but may also accept other HTTP methods by setting the \nmethods\n argument. For example:\n\n\n @action(methods=['post', 'delete'], detail=True)\n def unset_password(self, request, pk=None):\n ...\n\n\n\nThe two new actions will then be available at the urls \n^users/{pk}/set_password/$\n and \n^users/{pk}/unset_password/$\n\n\nTo view all extra actions, call the \n.get_extra_actions()\n method.\n\n\nReversing action URLs\n\n\nIf you need to get the URL of an action, use the \n.reverse_action()\n method. This is a convenience wrapper for \nreverse()\n, automatically passing the view's \nrequest\n object and prepending the \nurl_name\n with the \n.basename\n attribute.\n\n\nNote that the \nbasename\n is provided by the router during \nViewSet\n registration. If you are not using a router, then you must provide the \nbasename\n argument to the \n.as_view()\n method.\n\n\nUsing the example from the previous section:\n\n\n>>> view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nAlternatively, you can use the \nurl_name\n attribute set by the \n@action\n decorator.\n\n\n>>> view.reverse_action(view.set_password.url_name, args=['1'])\n'http://localhost:8000/api/users/1/set_password'\n\n\n\n\nThe \nurl_name\n argument for \n.reverse_action()\n should match the same argument to the \n@action\n decorator. Additionally, this method can be used to reverse the default actions, such as \nlist\n and \ncreate\n.\n\n\n\n\nAPI Reference\n\n\nViewSet\n\n\nThe \nViewSet\n class inherits from \nAPIView\n. You can use any of the standard attributes such as \npermission_classes\n, \nauthentication_classes\n in order to control the API policy on the viewset.\n\n\nThe \nViewSet\n class does not provide any implementations of actions. In order to use a \nViewSet\n class you'll override the class and define the action implementations explicitly.\n\n\nGenericViewSet\n\n\nThe \nGenericViewSet\n class inherits from \nGenericAPIView\n, and provides the default set of \nget_object\n, \nget_queryset\n methods and other generic view base behavior, but does not include any actions by default.\n\n\nIn order to use a \nGenericViewSet\n class you'll override the class and either mixin the required mixin classes, or define the action implementations explicitly.\n\n\nModelViewSet\n\n\nThe \nModelViewSet\n class inherits from \nGenericAPIView\n and includes implementations for various actions, by mixing in the behavior of the various mixin classes.\n\n\nThe actions provided by the \nModelViewSet\n class are \n.list()\n, \n.retrieve()\n, \n.create()\n, \n.update()\n, \n.partial_update()\n, and \n.destroy()\n.\n\n\nExample\n\n\nBecause \nModelViewSet\n extends \nGenericAPIView\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n\n\nNote that you can use any of the standard attributes or method overrides provided by \nGenericAPIView\n. For example, to use a \nViewSet\n that dynamically determines the queryset it should operate on, you might do something like this:\n\n\nclass AccountViewSet(viewsets.ModelViewSet):\n \"\"\"\n A simple ViewSet for viewing and editing the accounts\n associated with the user.\n \"\"\"\n serializer_class = AccountSerializer\n permission_classes = [IsAccountAdminOrReadOnly]\n\n def get_queryset(self):\n return self.request.user.accounts.all()\n\n\n\nNote however that upon removal of the \nqueryset\n property from your \nViewSet\n, any associated \nrouter\n will be unable to derive the base_name of your Model automatically, and so you will have to specify the \nbase_name\n kwarg as part of your \nrouter registration\n.\n\n\nAlso note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.\n\n\nReadOnlyModelViewSet\n\n\nThe \nReadOnlyModelViewSet\n class also inherits from \nGenericAPIView\n. As with \nModelViewSet\n it also includes implementations for various actions, but unlike \nModelViewSet\n only provides the 'read-only' actions, \n.list()\n and \n.retrieve()\n.\n\n\nExample\n\n\nAs with \nModelViewSet\n, you'll normally need to provide at least the \nqueryset\n and \nserializer_class\n attributes. For example:\n\n\nclass AccountViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A simple ViewSet for viewing accounts.\n \"\"\"\n queryset = Account.objects.all()\n serializer_class = AccountSerializer\n\n\n\nAgain, as with \nModelViewSet\n, you can use any of the standard attributes and method overrides available to \nGenericAPIView\n.\n\n\nCustom ViewSet base classes\n\n\nYou may need to provide custom \nViewSet\n classes that do not have the full set of \nModelViewSet\n actions, or that customize the behavior in some other way.\n\n\nExample\n\n\nTo create a base viewset class that provides \ncreate\n, \nlist\n and \nretrieve\n operations, inherit from \nGenericViewSet\n, and mixin the required actions:\n\n\nfrom rest_framework import mixins\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n mixins.ListModelMixin,\n mixins.RetrieveModelMixin,\n viewsets.GenericViewSet):\n \"\"\"\n A viewset that provides `retrieve`, `create`, and `list` actions.\n\n To use it, override the class and set the `.queryset` and\n `.serializer_class` attributes.\n \"\"\"\n pass\n\n\n\nBy creating your own base \nViewSet\n classes, you can provide common behavior that can be reused in multiple viewsets across your API.",
"title": "Viewsets"
},
{
@@ -907,17 +907,22 @@
},
{
"location": "/api-guide/viewsets/#viewset-actions",
- "text": "The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below: class UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass During dispatch the name of the current action is available via the .action attribute.\nYou may inspect .action 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):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]",
+ "text": "The default routers included with REST framework will provide routes for a standard set of create/retrieve/update/destroy style actions, as shown below: class UserViewSet(viewsets.ViewSet):\n \"\"\"\n Example empty viewset demonstrating the standard\n actions that will be handled by a router class.\n\n If you're using format suffixes, make sure to also include\n the `format=None` keyword argument for each action.\n \"\"\"\n\n def list(self, request):\n pass\n\n def create(self, request):\n pass\n\n def retrieve(self, request, pk=None):\n pass\n\n def update(self, request, pk=None):\n pass\n\n def partial_update(self, request, pk=None):\n pass\n\n def destroy(self, request, pk=None):\n pass",
"title": "ViewSet actions"
},
+ {
+ "location": "/api-guide/viewsets/#introspecting-viewset-actions",
+ "text": "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):\n \"\"\"\n Instantiates and returns the list of permissions that this view requires.\n \"\"\"\n if self.action == 'list':\n permission_classes = [IsAuthenticated]\n else:\n permission_classes = [IsAdmin]\n return [permission() for permission in permission_classes]",
+ "title": "Introspecting ViewSet actions"
+ },
{
"location": "/api-guide/viewsets/#marking-extra-actions-for-routing",
- "text": "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\nfrom rest_framework import status\nfrom rest_framework import viewsets\nfrom rest_framework.decorators import detail_route, list_route\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @detail_route(methods=['post'])\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @list_route()\n def recent_users(self, request):\n recent_users = User.objects.all().order('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data) The decorators can additionally take extra arguments that will be set for the routed view only. For example... @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... These decorators will route GET requests by default, but may also accept other HTTP methods, by using the methods argument. For example: @detail_route(methods=['post', 'delete'])\n def unset_password(self, request, pk=None):\n ... The two new actions will then be available at the urls ^users/{pk}/set_password/$ and ^users/{pk}/unset_password/$",
+ "text": "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. A more complete example of extra actions: from django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n\n @action(methods=['post'], detail=True)\n def set_password(self, request, pk=None):\n user = self.get_object()\n serializer = PasswordSerializer(data=request.data)\n if serializer.is_valid():\n user.set_password(serializer.data['password'])\n user.save()\n return Response({'status': 'password set'})\n else:\n return Response(serializer.errors,\n status=status.HTTP_400_BAD_REQUEST)\n\n @action(detail=False)\n def recent_users(self, request):\n recent_users = User.objects.all().order('-last_login')\n\n page = self.paginate_queryset(recent_users)\n if page is not None:\n serializer = self.get_serializer(page, many=True)\n return self.get_paginated_response(serializer.data)\n\n serializer = self.get_serializer(recent_users, many=True)\n return Response(serializer.data) The decorator can additionally take extra arguments that will be set for the routed view only. For example: @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... These decorator will route GET requests by default, but may also accept other HTTP methods by setting the methods argument. For example: @action(methods=['post', 'delete'], detail=True)\n def unset_password(self, request, pk=None):\n ... 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.",
"title": "Marking extra actions for routing"
},
{
"location": "/api-guide/viewsets/#reversing-action-urls",
- "text": "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. Note that the basename is provided by the router during ViewSet registration. If you are not using a router, then you must provide the basename argument to the .as_view() method. Using the example from the previous section: >>> view.reverse_action('set-password', args=['1'])\n'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.",
+ "text": "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. Note that the basename is provided by the router during ViewSet registration. If you are not using a router, then you must provide the basename argument to the .as_view() method. Using the example from the previous section: >>> view.reverse_action('set-password', args=['1'])\n'http://localhost:8000/api/users/1/set_password' Alternatively, you can use the url_name attribute set by the @action decorator. >>> view.reverse_action(view.set_password.url_name, args=['1'])\n'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 .",
"title": "Reversing action URLs"
},
{
@@ -967,7 +972,7 @@
},
{
"location": "/api-guide/routers/",
- "text": "Routers\n\n\n\n\nResource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nSome Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.\n\n\nREST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.\n\n\nUsage\n\n\nHere's an example of a simple URL conf, that uses \nSimpleRouter\n.\n\n\nfrom rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls\n\n\n\nThere are two mandatory arguments to the \nregister()\n method:\n\n\n\n\nprefix\n - The URL prefix to use for this set of routes.\n\n\nviewset\n - The viewset class.\n\n\n\n\nOptionally, you may also specify an additional argument:\n\n\n\n\nbase_name\n - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the \nqueryset\n attribute of the viewset, if it has one. Note that if the viewset does not include a \nqueryset\n attribute then you must set \nbase_name\n when registering the viewset.\n\n\n\n\nThe example above would generate the following URL patterns:\n\n\n\n\nURL pattern: \n^users/$\n Name: \n'user-list'\n\n\nURL pattern: \n^users/{pk}/$\n Name: \n'user-detail'\n\n\nURL pattern: \n^accounts/$\n Name: \n'account-list'\n\n\nURL pattern: \n^accounts/{pk}/$\n Name: \n'account-detail'\n\n\n\n\n\n\nNote\n: The \nbase_name\n argument is used to specify the initial part of the view name pattern. In the example above, that's the \nuser\n or \naccount\n part.\n\n\nTypically you won't \nneed\n to specify the \nbase_name\n argument, but if you have a viewset where you've defined a custom \nget_queryset\n method, then the viewset may not have a \n.queryset\n attribute set. If you try to register that viewset you'll see an error like this:\n\n\n'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.\n\n\n\nThis means you'll need to explicitly set the \nbase_name\n argument when registering the viewset, as it could not be automatically determined from the model name.\n\n\n\n\nUsing \ninclude\n with routers\n\n\nThe \n.urls\n attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.\n\n\nFor example, you can append \nrouter.urls\n to a list of existing views\u2026\n\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls\n\n\n\nAlternatively you can use Django's \ninclude\n function, like so\u2026\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n]\n\n\n\nRouter URL patterns can also be namespaces.\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include(router.urls, namespace='api')),\n]\n\n\n\nIf using namespacing with hyperlinked serializers you'll also need to ensure that any \nview_name\n parameters on the serializers correctly reflect the namespace. In the example above you'd need to include a parameter such as \nview_name='api:user-detail'\n for serializer fields hyperlinked to the user detail view.\n\n\nExtra link and actions\n\n\nAny methods on the viewset decorated with \n@detail_route\n or \n@list_route\n will also be routed.\nFor example, given a method like this on the \nUserViewSet\n class:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe following URL pattern would additionally be generated:\n\n\n\n\nURL pattern: \n^users/{pk}/set_password/$\n Name: \n'user-set-password'\n\n\n\n\nIf 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.\n\n\nFor example, if you want to change the URL for our custom action to \n^users/{pk}/change-password/$\n, you could write:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password')\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe above example would now generate the following URL pattern:\n\n\n\n\nURL pattern: \n^users/{pk}/change-password/$\n Name: \n'user-change-password'\n\n\n\n\nIn 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.\n\n\nFor example, if you want to change the name of our custom action to \n'user-change-password'\n, you could write:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password')\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe above example would now generate the following URL pattern:\n\n\n\n\nURL pattern: \n^users/{pk}/set_password/$\n Name: \n'user-change-password'\n\n\n\n\nYou can also use url_path and url_name parameters together to obtain extra control on URL generation for custom views.\n\n\nFor more information see the viewset documentation on \nmarking extra actions for routing\n.\n\n\nAPI Guide\n\n\nSimpleRouter\n\n\nThis router includes routes for the standard set of \nlist\n, \ncreate\n, \nretrieve\n, \nupdate\n, \npartial_update\n and \ndestroy\n actions. The viewset can also mark additional methods to be routed, using the \n@detail_route\n or \n@list_route\n decorators.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n{prefix}/\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{methodname}/\nGET, or as specified by `methods` argument\n`@list_route` decorated method\n{basename}-{methodname}\n\n \n{prefix}/{lookup}/\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{methodname}/\nGET, or as specified by `methods` argument\n`@detail_route` decorated method\n{basename}-{methodname}\n\n\n\n\n\nBy default the URLs created by \nSimpleRouter\n are appended with a trailing slash.\nThis behavior can be modified by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router. For example:\n\n\nrouter = SimpleRouter(trailing_slash=False)\n\n\n\nTrailing 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.\n\n\nThe router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the \nlookup_value_regex\n attribute on the viewset. For example, you can limit the lookup to valid UUIDs:\n\n\nclass MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'\n\n\n\nDefaultRouter\n\n\nThis router is similar to \nSimpleRouter\n 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 \n.json\n style format suffixes.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n[.format]\nGET\nautomatically generated root view\napi-root\n\n \n{prefix}/[.format]\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{methodname}/[.format]\nGET, or as specified by `methods` argument\n`@list_route` decorated method\n{basename}-{methodname}\n\n \n{prefix}/{lookup}/[.format]\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{methodname}/[.format]\nGET, or as specified by `methods` argument\n`@detail_route` decorated method\n{basename}-{methodname}\n\n\n\n\n\nAs with \nSimpleRouter\n the trailing slashes on the URL routes can be removed by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router.\n\n\nrouter = DefaultRouter(trailing_slash=False)\n\n\n\nCustom Routers\n\n\nImplementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.\n\n\nThe simplest way to implement a custom router is to subclass one of the existing router classes. The \n.routes\n attribute is used to template the URL patterns that will be mapped to each viewset. The \n.routes\n attribute is a list of \nRoute\n named tuples.\n\n\nThe arguments to the \nRoute\n named tuple are:\n\n\nurl\n: A string representing the URL to be routed. May include the following format strings:\n\n\n\n\n{prefix}\n - The URL prefix to use for this set of routes.\n\n\n{lookup}\n - The lookup field used to match against a single instance.\n\n\n{trailing_slash}\n - Either a '/' or an empty string, depending on the \ntrailing_slash\n argument.\n\n\n\n\nmapping\n: A mapping of HTTP method names to the view methods\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format string:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the \nsuffix\n argument is reserved for identifying the viewset type, used when generating the view name and breadcrumb links.\n\n\nCustomizing dynamic routes\n\n\nYou can also customize how the \n@list_route\n and \n@detail_route\n decorators are routed.\nTo route either or both of these decorators, include a \nDynamicListRoute\n and/or \nDynamicDetailRoute\n named tuple in the \n.routes\n list.\n\n\nThe arguments to \nDynamicListRoute\n and \nDynamicDetailRoute\n are:\n\n\nurl\n: A string representing the URL to be routed. May include the same format strings as \nRoute\n, and additionally accepts the \n{methodname}\n and \n{methodnamehyphen}\n format strings.\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format strings: \n{basename}\n, \n{methodname}\n and \n{methodnamehyphen}\n.\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view.\n\n\nExample\n\n\nThe following example will only route to the \nlist\n and \nretrieve\n actions, and does not use the trailing slash convention.\n\n\nfrom rest_framework.routers import Route, DynamicDetailRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicDetailRoute(\n url=r'^{prefix}/{lookup}/{methodnamehyphen}$',\n name='{basename}-{methodnamehyphen}',\n initkwargs={}\n )\n ]\n\n\n\nLet's take a look at the routes our \nCustomReadOnlyRouter\n would generate for a simple viewset.\n\n\nviews.py\n:\n\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @detail_route()\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups])\n\n\n\nurls.py\n:\n\n\nrouter = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls\n\n\n\nThe following mappings would be generated...\n\n\n\n \nURL\nHTTP Method\nAction\nURL Name\n\n \n/users\nGET\nlist\nuser-list\n\n \n/users/{username}\nGET\nretrieve\nuser-detail\n\n \n/users/{username}/group-names\nGET\ngroup_names\nuser-group-names\n\n\n\n\n\nFor another example of setting the \n.routes\n attribute, see the source code for the \nSimpleRouter\n class.\n\n\nAdvanced custom routers\n\n\nIf you want to provide totally custom behavior, you can override \nBaseRouter\n and override the \nget_urls(self)\n method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the \nself.registry\n attribute.\n\n\nYou may also want to override the \nget_default_base_name(self, viewset)\n method, or else always explicitly set the \nbase_name\n argument when registering your viewsets with the router.\n\n\nThird Party Packages\n\n\nThe following third party packages are also available.\n\n\nDRF Nested Routers\n\n\nThe \ndrf-nested-routers package\n provides routers and relationship fields for working with nested resources.\n\n\nModelRouter (wq.db.rest)\n\n\nThe \nwq.db package\n provides an advanced \nModelRouter\n class (and singleton instance) that extends \nDefaultRouter\n with a \nregister_model()\n API. Much like Django's \nadmin.site.register\n, the only required argument to \nrest.router.register_model\n is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.\n\n\nfrom wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)\n\n\n\nDRF-extensions\n\n\nThe \nDRF-extensions\n package\n provides \nrouters\n for creating \nnested viewsets\n, \ncollection level controllers\n with \ncustomizable endpoint names\n.",
+ "text": "Routers\n\n\n\n\nResource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your index... a resourceful route declares them in a single line of code.\n\n\n\u2014 \nRuby on Rails Documentation\n\n\n\n\nSome Web frameworks such as Rails provide functionality for automatically determining how the URLs for an application should be mapped to the logic that deals with handling incoming requests.\n\n\nREST framework adds support for automatic URL routing to Django, and provides you with a simple, quick and consistent way of wiring your view logic to a set of URLs.\n\n\nUsage\n\n\nHere's an example of a simple URL conf, that uses \nSimpleRouter\n.\n\n\nfrom rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls\n\n\n\nThere are two mandatory arguments to the \nregister()\n method:\n\n\n\n\nprefix\n - The URL prefix to use for this set of routes.\n\n\nviewset\n - The viewset class.\n\n\n\n\nOptionally, you may also specify an additional argument:\n\n\n\n\nbase_name\n - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the \nqueryset\n attribute of the viewset, if it has one. Note that if the viewset does not include a \nqueryset\n attribute then you must set \nbase_name\n when registering the viewset.\n\n\n\n\nThe example above would generate the following URL patterns:\n\n\n\n\nURL pattern: \n^users/$\n Name: \n'user-list'\n\n\nURL pattern: \n^users/{pk}/$\n Name: \n'user-detail'\n\n\nURL pattern: \n^accounts/$\n Name: \n'account-list'\n\n\nURL pattern: \n^accounts/{pk}/$\n Name: \n'account-detail'\n\n\n\n\n\n\nNote\n: The \nbase_name\n argument is used to specify the initial part of the view name pattern. In the example above, that's the \nuser\n or \naccount\n part.\n\n\nTypically you won't \nneed\n to specify the \nbase_name\n argument, but if you have a viewset where you've defined a custom \nget_queryset\n method, then the viewset may not have a \n.queryset\n attribute set. If you try to register that viewset you'll see an error like this:\n\n\n'base_name' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.\n\n\n\nThis means you'll need to explicitly set the \nbase_name\n argument when registering the viewset, as it could not be automatically determined from the model name.\n\n\n\n\nUsing \ninclude\n with routers\n\n\nThe \n.urls\n attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs.\n\n\nFor example, you can append \nrouter.urls\n to a list of existing views\u2026\n\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls\n\n\n\nAlternatively you can use Django's \ninclude\n function, like so\u2026\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n]\n\n\n\nYou may use \ninclude\n with an application namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n]\n\n\n\nOr both an application and instance namespace:\n\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n]\n\n\n\nSee Django's \nURL namespaces docs\n and the \ninclude\n API reference\n for more details.\n\n\n\n\nNote\n: If using namespacing with hyperlinked serializers you'll also need to ensure that any \nview_name\n parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as\n\nview_name='app_name:user-detail'\n for serializer fields hyperlinked to the user detail view.\n\n\nThe automatic \nview_name\n generation uses a pattern like \n%(model_name)-detail\n. Unless your models names actually clash\nyou may be better off \nnot\n namespacing your Django REST Framework views when using hyperlinked serializers.\n\n\n\n\nRouting for extra actions\n\n\nA viewset may \nmark extra actions for routing\n by decorating a method with the \n@action\n decorator. These extra actions will be included in the generated routes. For example, given the \nset_password\n method on the \nUserViewSet\n class:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe following route would be generated:\n\n\n\n\nURL pattern: \n^users/{pk}/set_password/$\n\n\nURL name: \n'user-set-password'\n\n\n\n\nBy default, the URL pattern is based on the method name, and the URL name is the combination of the \nViewSet.basename\n and the hyphenated method name.\nIf you don't want to use the defaults for either of these values, you can instead provide the \nurl_path\n and \nurl_name\n arguments to the \n@action\n decorator.\n\n\nFor example, if you want to change the URL for our custom action to \n^users/{pk}/change-password/$\n, you could write:\n\n\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ...\n\n\n\nThe above example would now generate the following URL pattern:\n\n\n\n\nURL path: \n^users/{pk}/change-password/$\n\n\nURL name: \n'user-change_password'\n\n\n\n\nAPI Guide\n\n\nSimpleRouter\n\n\nThis router includes routes for the standard set of \nlist\n, \ncreate\n, \nretrieve\n, \nupdate\n, \npartial_update\n and \ndestroy\n actions. The viewset can also mark additional methods to be routed, using the \n@action\n decorator.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n{prefix}/\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nBy default the URLs created by \nSimpleRouter\n are appended with a trailing slash.\nThis behavior can be modified by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router. For example:\n\n\nrouter = SimpleRouter(trailing_slash=False)\n\n\n\nTrailing 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.\n\n\nThe router will match lookup values containing any characters except slashes and period characters. For a more restrictive (or lenient) lookup pattern, set the \nlookup_value_regex\n attribute on the viewset. For example, you can limit the lookup to valid UUIDs:\n\n\nclass MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'\n\n\n\nDefaultRouter\n\n\nThis router is similar to \nSimpleRouter\n 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 \n.json\n style format suffixes.\n\n\n\n \nURL Style\nHTTP Method\nAction\nURL Name\n\n \n[.format]\nGET\nautomatically generated root view\napi-root\n\n \n{prefix}/[.format]\nGET\nlist\n{basename}-list\n\n \nPOST\ncreate\n\n \n{prefix}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=False)` decorated method\n{basename}-{url_name}\n\n \n{prefix}/{lookup}/[.format]\nGET\nretrieve\n{basename}-detail\n\n \nPUT\nupdate\n\n \nPATCH\npartial_update\n\n \nDELETE\ndestroy\n\n \n{prefix}/{lookup}/{url_path}/[.format]\nGET, or as specified by `methods` argument\n`@action(detail=True)` decorated method\n{basename}-{url_name}\n\n\n\n\n\nAs with \nSimpleRouter\n the trailing slashes on the URL routes can be removed by setting the \ntrailing_slash\n argument to \nFalse\n when instantiating the router.\n\n\nrouter = DefaultRouter(trailing_slash=False)\n\n\n\nCustom Routers\n\n\nImplementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view.\n\n\nThe simplest way to implement a custom router is to subclass one of the existing router classes. The \n.routes\n attribute is used to template the URL patterns that will be mapped to each viewset. The \n.routes\n attribute is a list of \nRoute\n named tuples.\n\n\nThe arguments to the \nRoute\n named tuple are:\n\n\nurl\n: A string representing the URL to be routed. May include the following format strings:\n\n\n\n\n{prefix}\n - The URL prefix to use for this set of routes.\n\n\n{lookup}\n - The lookup field used to match against a single instance.\n\n\n{trailing_slash}\n - Either a '/' or an empty string, depending on the \ntrailing_slash\n argument.\n\n\n\n\nmapping\n: A mapping of HTTP method names to the view methods\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format string:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view. Note that the \ndetail\n, \nbasename\n, and \nsuffix\n arguments are reserved for viewset introspection and are also used by the browsable API to generate the view name and breadcrumb links.\n\n\nCustomizing dynamic routes\n\n\nYou can also customize how the \n@action\n decorator is routed. Include the \nDynamicRoute\n named tuple in the \n.routes\n list, setting the \ndetail\n argument as appropriate for the list-based and detail-based routes. In addition to \ndetail\n, the arguments to \nDynamicRoute\n are:\n\n\nurl\n: A string representing the URL to be routed. May include the same format strings as \nRoute\n, and additionally accepts the \n{url_path}\n format string.\n\n\nname\n: The name of the URL as used in \nreverse\n calls. May include the following format strings:\n\n\n\n\n{basename}\n - The base to use for the URL names that are created.\n\n\n{url_name}\n - The \nurl_name\n provided to the \n@action\n.\n\n\n\n\ninitkwargs\n: A dictionary of any additional arguments that should be passed when instantiating the view.\n\n\nExample\n\n\nThe following example will only route to the \nlist\n and \nretrieve\n actions, and does not use the trailing slash convention.\n\n\nfrom rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ]\n\n\n\nLet's take a look at the routes our \nCustomReadOnlyRouter\n would generate for a simple viewset.\n\n\nviews.py\n:\n\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups])\n\n\n\nurls.py\n:\n\n\nrouter = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls\n\n\n\nThe following mappings would be generated...\n\n\n\n \nURL\nHTTP Method\nAction\nURL Name\n\n \n/users\nGET\nlist\nuser-list\n\n \n/users/{username}\nGET\nretrieve\nuser-detail\n\n \n/users/{username}/group-names\nGET\ngroup_names\nuser-group-names\n\n\n\n\n\nFor another example of setting the \n.routes\n attribute, see the source code for the \nSimpleRouter\n class.\n\n\nAdvanced custom routers\n\n\nIf you want to provide totally custom behavior, you can override \nBaseRouter\n and override the \nget_urls(self)\n method. The method should inspect the registered viewsets and return a list of URL patterns. The registered prefix, viewset and basename tuples may be inspected by accessing the \nself.registry\n attribute.\n\n\nYou may also want to override the \nget_default_base_name(self, viewset)\n method, or else always explicitly set the \nbase_name\n argument when registering your viewsets with the router.\n\n\nThird Party Packages\n\n\nThe following third party packages are also available.\n\n\nDRF Nested Routers\n\n\nThe \ndrf-nested-routers package\n provides routers and relationship fields for working with nested resources.\n\n\nModelRouter (wq.db.rest)\n\n\nThe \nwq.db package\n provides an advanced \nModelRouter\n class (and singleton instance) that extends \nDefaultRouter\n with a \nregister_model()\n API. Much like Django's \nadmin.site.register\n, the only required argument to \nrest.router.register_model\n is a model class. Reasonable defaults for a url prefix, serializer, and viewset will be inferred from the model and global configuration.\n\n\nfrom wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)\n\n\n\nDRF-extensions\n\n\nThe \nDRF-extensions\n package\n provides \nrouters\n for creating \nnested viewsets\n, \ncollection level controllers\n with \ncustomizable endpoint names\n.",
"title": "Routers"
},
{
@@ -982,13 +987,13 @@
},
{
"location": "/api-guide/routers/#using-include-with-routers",
- "text": "The .urls attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs. For example, you can append router.urls to a list of existing views\u2026 router = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls Alternatively you can use Django's include function, like so\u2026 urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n] Router URL patterns can also be namespaces. urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include(router.urls, namespace='api')),\n] 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.",
+ "text": "The .urls attribute on a router instance is simply a standard list of URL patterns. There are a number of different styles for how you can include these URLs. For example, you can append router.urls to a list of existing views\u2026 router = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls Alternatively you can use Django's include function, like so\u2026 urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^', include(router.urls)),\n] You may use include with an application namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'))),\n] Or both an application and instance namespace: urlpatterns = [\n url(r'^forgot-password/$', ForgotPasswordFormView.as_view()),\n url(r'^api/', include((router.urls, 'app_name'), namespace='instance_name')),\n] See Django's URL namespaces docs and the include API reference for more details. Note : If using namespacing with hyperlinked serializers you'll also need to ensure that any view_name parameters\non the serializers correctly reflect the namespace. In the examples above you'd need to include a parameter such as view_name='app_name:user-detail' for serializer fields hyperlinked to the user detail view. The automatic view_name generation uses a pattern like %(model_name)-detail . Unless your models names actually clash\nyou may be better off not namespacing your Django REST Framework views when using hyperlinked serializers.",
"title": "Using include with routers"
},
{
- "location": "/api-guide/routers/#extra-link-and-actions",
- "text": "Any methods on the viewset decorated with @detail_route or @list_route will also be routed.\nFor example, given a method like this on the UserViewSet class: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The following URL pattern would additionally be generated: URL pattern: ^users/{pk}/set_password/$ 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. 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\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_path='change-password')\n def set_password(self, request, pk=None):\n ... 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\nfrom rest_framework.decorators import detail_route\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf], url_name='change-password')\n def set_password(self, request, pk=None):\n ... 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. For more information see the viewset documentation on marking extra actions for routing .",
- "title": "Extra link and actions"
+ "location": "/api-guide/routers/#routing-for-extra-actions",
+ "text": "A viewset may mark extra actions for routing by decorating a method with the @action decorator. These extra actions will be included in the generated routes. For example, given the set_password method on the UserViewSet class: from myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n def set_password(self, request, pk=None):\n ... The following route would be generated: URL pattern: ^users/{pk}/set_password/$ URL name: 'user-set-password' 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.\nIf you don't want to use the defaults for either of these values, you can instead provide 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\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n ...\n\n @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n url_path='change-password', url_name='change_password')\n def set_password(self, request, pk=None):\n ... The above example would now generate the following URL pattern: URL path: ^users/{pk}/change-password/$ URL name: 'user-change_password'",
+ "title": "Routing for extra actions"
},
{
"location": "/api-guide/routers/#api-guide",
@@ -997,27 +1002,27 @@
},
{
"location": "/api-guide/routers/#simplerouter",
- "text": "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. \n URL Style HTTP Method Action URL Name \n {prefix}/ GET list {basename}-list \n POST create \n {prefix}/{methodname}/ GET, or as specified by `methods` argument `@list_route` decorated method {basename}-{methodname} \n {prefix}/{lookup}/ GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{methodname}/ GET, or as specified by `methods` argument `@detail_route` decorated method {basename}-{methodname} By default the URLs created by SimpleRouter are appended with a trailing slash.\nThis behavior can be modified by setting the trailing_slash argument to False when instantiating the router. For example: router = SimpleRouter(trailing_slash=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):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'",
+ "text": "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. \n URL Style HTTP Method Action URL Name \n {prefix}/ GET list {basename}-list \n POST create \n {prefix}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/ GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/ GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} By default the URLs created by SimpleRouter are appended with a trailing slash.\nThis behavior can be modified by setting the trailing_slash argument to False when instantiating the router. For example: router = SimpleRouter(trailing_slash=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):\n lookup_field = 'my_model_id'\n lookup_value_regex = '[0-9a-f]{32}'",
"title": "SimpleRouter"
},
{
"location": "/api-guide/routers/#defaultrouter",
- "text": "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. \n URL Style HTTP Method Action URL Name \n [.format] GET automatically generated root view api-root \n {prefix}/[.format] GET list {basename}-list \n POST create \n {prefix}/{methodname}/[.format] GET, or as specified by `methods` argument `@list_route` decorated method {basename}-{methodname} \n {prefix}/{lookup}/[.format] GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{methodname}/[.format] GET, or as specified by `methods` argument `@detail_route` decorated method {basename}-{methodname} 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. router = DefaultRouter(trailing_slash=False)",
+ "text": "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. \n URL Style HTTP Method Action URL Name \n [.format] GET automatically generated root view api-root \n {prefix}/[.format] GET list {basename}-list \n POST create \n {prefix}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=False)` decorated method {basename}-{url_name} \n {prefix}/{lookup}/[.format] GET retrieve {basename}-detail \n PUT update \n PATCH partial_update \n DELETE destroy \n {prefix}/{lookup}/{url_path}/[.format] GET, or as specified by `methods` argument `@action(detail=True)` decorated method {basename}-{url_name} 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. router = DefaultRouter(trailing_slash=False)",
"title": "DefaultRouter"
},
{
"location": "/api-guide/routers/#custom-routers",
- "text": "Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view. The simplest way to implement a custom router is to subclass one of the existing router classes. The .routes attribute is used to template the URL patterns that will be mapped to each viewset. The .routes attribute is a list of Route named tuples. The arguments to the Route named tuple are: url : A string representing the URL to be routed. May include the following format strings: {prefix} - The URL prefix to use for this set of routes. {lookup} - The lookup field used to match against a single instance. {trailing_slash} - Either a '/' or an empty string, depending on the trailing_slash argument. mapping : A mapping of HTTP method names to the view methods name : The name of the URL as used in reverse calls. May include the following format string: {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.",
+ "text": "Implementing a custom router isn't something you'd need to do very often, but it can be useful if you have specific requirements about how the URLs for your API are structured. Doing so allows you to encapsulate the URL structure in a reusable way that ensures you don't have to write your URL patterns explicitly for each new view. The simplest way to implement a custom router is to subclass one of the existing router classes. The .routes attribute is used to template the URL patterns that will be mapped to each viewset. The .routes attribute is a list of Route named tuples. The arguments to the Route named tuple are: url : A string representing the URL to be routed. May include the following format strings: {prefix} - The URL prefix to use for this set of routes. {lookup} - The lookup field used to match against a single instance. {trailing_slash} - Either a '/' or an empty string, depending on the trailing_slash argument. mapping : A mapping of HTTP method names to the view methods name : The name of the URL as used in reverse calls. May include the following format string: {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 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.",
"title": "Custom Routers"
},
{
"location": "/api-guide/routers/#customizing-dynamic-routes",
- "text": "You can also customize how the @list_route and @detail_route decorators are routed.\nTo 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.",
+ "text": "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: 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. name : The name of the URL as used in reverse calls. May include the following format strings: {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.",
"title": "Customizing dynamic routes"
},
{
"location": "/api-guide/routers/#example",
- "text": "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\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicDetailRoute(\n url=r'^{prefix}/{lookup}/{methodnamehyphen}$',\n name='{basename}-{methodnamehyphen}',\n initkwargs={}\n )\n ] Let's take a look at the routes our CustomReadOnlyRouter would generate for a simple viewset. views.py : class UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @detail_route()\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups]) urls.py : router = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls The following mappings would be generated... \n URL HTTP Method Action URL Name \n /users GET list user-list \n /users/{username} GET retrieve user-detail \n /users/{username}/group-names GET group_names user-group-names For another example of setting the .routes attribute, see the source code for the SimpleRouter class.",
+ "text": "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, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n \"\"\"\n A router for read-only APIs, which doesn't use trailing slashes.\n \"\"\"\n routes = [\n Route(\n url=r'^{prefix}$',\n mapping={'get': 'list'},\n name='{basename}-list',\n initkwargs={'suffix': 'List'}\n ),\n Route(\n url=r'^{prefix}/{lookup}$',\n mapping={'get': 'retrieve'},\n name='{basename}-detail',\n initkwargs={'suffix': 'Detail'}\n ),\n DynamicRoute(\n url=r'^{prefix}/{lookup}/{url_path}$',\n name='{basename}-{url_name}',\n detail=True,\n initkwargs={}\n )\n ] Let's take a look at the routes our CustomReadOnlyRouter would generate for a simple viewset. views.py : class UserViewSet(viewsets.ReadOnlyModelViewSet):\n \"\"\"\n A viewset that provides the standard actions\n \"\"\"\n queryset = User.objects.all()\n serializer_class = UserSerializer\n lookup_field = 'username'\n\n @action(detail=True)\n def group_names(self, request, pk=None):\n \"\"\"\n Returns a list of all the group names that the given\n user belongs to.\n \"\"\"\n user = self.get_object()\n groups = user.groups.all()\n return Response([group.name for group in groups]) urls.py : router = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls The following mappings would be generated... \n URL HTTP Method Action URL Name \n /users GET list user-list \n /users/{username} GET retrieve user-detail \n /users/{username}/group-names GET group_names user-group-names For another example of setting the .routes attribute, see the source code for the SimpleRouter class.",
"title": "Example"
},
{
@@ -1352,7 +1357,7 @@
},
{
"location": "/api-guide/serializers/",
- "text": "Serializers\n\n\n\n\nExpanding the usefulness of the serializers is something that we would\nlike to address. However, it's not a trivial problem, and it\nwill take some serious design work.\n\n\n\u2014 Russell Keith-Magee, \nDjango users group\n\n\n\n\nSerializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into \nJSON\n, \nXML\n or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.\n\n\nThe serializers in REST framework work very similarly to Django's \nForm\n and \nModelForm\n classes. We provide a \nSerializer\n class which gives you a powerful, generic way to control the output of your responses, as well as a \nModelSerializer\n class which provides a useful shortcut for creating serializers that deal with model instances and querysets.\n\n\nDeclaring Serializers\n\n\nLet's start by creating a simple object we can use for example purposes:\n\n\nfrom datetime import datetime\n\nclass Comment(object):\n def __init__(self, email, content, created=None):\n self.email = email\n self.content = content\n self.created = created or datetime.now()\n\ncomment = Comment(email='leila@example.com', content='foo bar')\n\n\n\nWe'll declare a serializer that we can use to serialize and deserialize data that corresponds to \nComment\n objects.\n\n\nDeclaring a serializer looks very similar to declaring a form:\n\n\nfrom rest_framework import serializers\n\nclass CommentSerializer(serializers.Serializer):\n email = serializers.EmailField()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nSerializing objects\n\n\nWe can now use \nCommentSerializer\n to serialize a comment, or list of comments. Again, using the \nSerializer\n class looks a lot like using a \nForm\n class.\n\n\nserializer = CommentSerializer(comment)\nserializer.data\n# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}\n\n\n\nAt this point we've translated the model instance into Python native datatypes. To finalise the serialization process we render the data into \njson\n.\n\n\nfrom rest_framework.renderers import JSONRenderer\n\njson = JSONRenderer().render(serializer.data)\njson\n# b'{\"email\":\"leila@example.com\",\"content\":\"foo bar\",\"created\":\"2016-01-27T15:17:10.375877\"}'\n\n\n\nDeserializing objects\n\n\nDeserialization is similar. First we parse a stream into Python native datatypes...\n\n\nfrom django.utils.six import BytesIO\nfrom rest_framework.parsers import JSONParser\n\nstream = BytesIO(json)\ndata = JSONParser().parse(stream)\n\n\n\n...then we restore those native datatypes into a dictionary of validated data.\n\n\nserializer = CommentSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}\n\n\n\nSaving instances\n\n\nIf we want to be able to return complete object instances based on the validated data we need to implement one or both of the \n.create()\n and \n.update()\n methods. For example:\n\n\nclass CommentSerializer(serializers.Serializer):\n email = serializers.EmailField()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n def create(self, validated_data):\n return Comment(**validated_data)\n\n def update(self, instance, validated_data):\n instance.email = validated_data.get('email', instance.email)\n instance.content = validated_data.get('content', instance.content)\n instance.created = validated_data.get('created', instance.created)\n return instance\n\n\n\nIf your object instances correspond to Django models you'll also want to ensure that these methods save the object to the database. For example, if \nComment\n was a Django model, the methods might look like this:\n\n\n def create(self, validated_data):\n return Comment.objects.create(**validated_data)\n\n def update(self, instance, validated_data):\n instance.email = validated_data.get('email', instance.email)\n instance.content = validated_data.get('content', instance.content)\n instance.created = validated_data.get('created', instance.created)\n instance.save()\n return instance\n\n\n\nNow when deserializing data, we can call \n.save()\n to return an object instance, based on the validated data.\n\n\ncomment = serializer.save()\n\n\n\nCalling \n.save()\n will either create a new instance, or update an existing instance, depending on if an existing instance was passed when instantiating the serializer class:\n\n\n# .save() will create a new instance.\nserializer = CommentSerializer(data=data)\n\n# .save() will update the existing `comment` instance.\nserializer = CommentSerializer(comment, data=data)\n\n\n\nBoth the \n.create()\n and \n.update()\n methods are optional. You can implement either neither, one, or both of them, depending on the use-case for your serializer class.\n\n\nPassing additional attributes to \n.save()\n\n\nSometimes you'll want your view code to be able to inject additional data at the point of saving the instance. This additional data might include information like the current user, the current time, or anything else that is not part of the request data.\n\n\nYou can do so by including additional keyword arguments when calling \n.save()\n. For example:\n\n\nserializer.save(owner=request.user)\n\n\n\nAny additional keyword arguments will be included in the \nvalidated_data\n argument when \n.create()\n or \n.update()\n are called.\n\n\nOverriding \n.save()\n directly.\n\n\nIn some cases the \n.create()\n and \n.update()\n method names may not be meaningful. For example, in a contact form we may not be creating new instances, but instead sending an email or other message.\n\n\nIn these cases you might instead choose to override \n.save()\n directly, as being more readable and meaningful.\n\n\nFor example:\n\n\nclass ContactForm(serializers.Serializer):\n email = serializers.EmailField()\n message = serializers.CharField()\n\n def save(self):\n email = self.validated_data['email']\n message = self.validated_data['message']\n send_email(from=email, message=message)\n\n\n\nNote that in the case above we're now having to access the serializer \n.validated_data\n property directly.\n\n\nValidation\n\n\nWhen deserializing data, you always need to call \nis_valid()\n before attempting to access the validated data, or save an object instance. If any validation errors occur, the \n.errors\n property will contain a dictionary representing the resulting error messages. For example:\n\n\nserializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}\n\n\n\nEach key in the dictionary will be the field name, and the values will be lists of strings of any error messages corresponding to that field. The \nnon_field_errors\n key may also be present, and will list any general validation errors. The name of the \nnon_field_errors\n key may be customized using the \nNON_FIELD_ERRORS_KEY\n REST framework setting.\n\n\nWhen deserializing a list of items, errors will be returned as a list of dictionaries representing each of the deserialized items.\n\n\nRaising an exception on invalid data\n\n\nThe \n.is_valid()\n method takes an optional \nraise_exception\n flag that will cause it to raise a \nserializers.ValidationError\n exception if there are validation errors.\n\n\nThese exceptions are automatically dealt with by the default exception handler that REST framework provides, and will return \nHTTP 400 Bad Request\n responses by default.\n\n\n# Return a 400 response if the data was invalid.\nserializer.is_valid(raise_exception=True)\n\n\n\nField-level validation\n\n\nYou can specify custom field-level validation by adding \n.validate_\n methods to your \nSerializer\n subclass. These are similar to the \n.clean_\n methods on Django forms.\n\n\nThese methods take a single argument, which is the field value that requires validation.\n\n\nYour \nvalidate_\n methods should return the validated value or raise a \nserializers.ValidationError\n. For example:\n\n\nfrom rest_framework import serializers\n\nclass BlogPostSerializer(serializers.Serializer):\n title = serializers.CharField(max_length=100)\n content = serializers.CharField()\n\n def validate_title(self, value):\n \"\"\"\n Check that the blog post is about Django.\n \"\"\"\n if 'django' not in value.lower():\n raise serializers.ValidationError(\"Blog post is not about Django\")\n return value\n\n\n\n\n\nNote:\n If your \n\n is declared on your serializer with the parameter \nrequired=False\n then this validation step will not take place if the field is not included.\n\n\n\n\nObject-level validation\n\n\nTo do any other validation that requires access to multiple fields, add a method called \n.validate()\n to your \nSerializer\n subclass. This method takes a single argument, which is a dictionary of field values. It should raise a \nValidationError\n if necessary, or just return the validated values. For example:\n\n\nfrom rest_framework import serializers\n\nclass EventSerializer(serializers.Serializer):\n description = serializers.CharField(max_length=100)\n start = serializers.DateTimeField()\n finish = serializers.DateTimeField()\n\n def validate(self, data):\n \"\"\"\n Check that the start is before the stop.\n \"\"\"\n if data['start'] > data['finish']:\n raise serializers.ValidationError(\"finish must occur after start\")\n return data\n\n\n\nValidators\n\n\nIndividual fields on a serializer can include validators, by declaring them on the field instance, for example:\n\n\ndef multiple_of_ten(value):\n if value % 10 != 0:\n raise serializers.ValidationError('Not a multiple of ten')\n\nclass GameRecord(serializers.Serializer):\n score = IntegerField(validators=[multiple_of_ten])\n ...\n\n\n\nSerializer classes can also include reusable validators that are applied to the complete set of field data. These validators are included by declaring them on an inner \nMeta\n class, like so:\n\n\nclass EventSerializer(serializers.Serializer):\n name = serializers.CharField()\n room_number = serializers.IntegerField(choices=[101, 102, 103, 201])\n date = serializers.DateField()\n\n class Meta:\n # Each room only has one event per day.\n validators = UniqueTogetherValidator(\n queryset=Event.objects.all(),\n fields=['room_number', 'date']\n )\n\n\n\nFor more information see the \nvalidators documentation\n.\n\n\nAccessing the initial data and instance\n\n\nWhen passing an initial object or queryset to a serializer instance, the object will be made available as \n.instance\n. If no initial object is passed then the \n.instance\n attribute will be \nNone\n.\n\n\nWhen passing data to a serializer instance, the unmodified data will be made available as \n.initial_data\n. If the data keyword argument is not passed then the \n.initial_data\n attribute will not exist.\n\n\nPartial updates\n\n\nBy default, serializers must be passed values for all required fields or they will raise validation errors. You can use the \npartial\n argument in order to allow partial updates.\n\n\n# Update `comment` with partial data\nserializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)\n\n\n\nDealing with nested objects\n\n\nThe previous examples are fine for dealing with objects that only have simple datatypes, but sometimes we also need to be able to represent more complex objects, where some of the attributes of an object might not be simple datatypes such as strings, dates or integers.\n\n\nThe \nSerializer\n class is itself a type of \nField\n, and can be used to represent relationships where one object type is nested inside another.\n\n\nclass UserSerializer(serializers.Serializer):\n email = serializers.EmailField()\n username = serializers.CharField(max_length=100)\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer()\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nIf a nested representation may optionally accept the \nNone\n value you should pass the \nrequired=False\n flag to the nested serializer.\n\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer(required=False) # May be an anonymous user.\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nSimilarly if a nested representation should be a list of items, you should pass the \nmany=True\n flag to the nested serialized.\n\n\nclass CommentSerializer(serializers.Serializer):\n user = UserSerializer(required=False)\n edits = EditItemSerializer(many=True) # A nested list of 'edit' items.\n content = serializers.CharField(max_length=200)\n created = serializers.DateTimeField()\n\n\n\nWritable nested representations\n\n\nWhen dealing with nested representations that support deserializing the data, any errors with nested objects will be nested under the field name of the nested object.\n\n\nserializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}\n\n\n\nSimilarly, the \n.validated_data\n property will include nested data structures.\n\n\nWriting \n.create()\n methods for nested representations\n\n\nIf you're supporting writable nested representations you'll need to write \n.create()\n or \n.update()\n methods that handle saving multiple objects.\n\n\nThe following example demonstrates how you might handle creating a user with a nested profile object.\n\n\nclass UserSerializer(serializers.ModelSerializer):\n profile = ProfileSerializer()\n\n class Meta:\n model = User\n fields = ('username', 'email', 'profile')\n\n def create(self, validated_data):\n profile_data = validated_data.pop('profile')\n user = User.objects.create(**validated_data)\n Profile.objects.create(user=user, **profile_data)\n return user\n\n\n\nWriting \n.update()\n methods for nested representations\n\n\nFor updates you'll want to think carefully about how to handle updates to relationships. For example if the data for the relationship is \nNone\n, or not provided, which of the following should occur?\n\n\n\n\nSet the relationship to \nNULL\n in the database.\n\n\nDelete the associated instance.\n\n\nIgnore the data and leave the instance as it is.\n\n\nRaise a validation error.\n\n\n\n\nHere's an example for an \n.update()\n method on our previous \nUserSerializer\n class.\n\n\n def update(self, instance, validated_data):\n profile_data = validated_data.pop('profile')\n # Unless the application properly enforces that this field is\n # always set, the follow could raise a `DoesNotExist`, which\n # would need to be handled.\n profile = instance.profile\n\n instance.username = validated_data.get('username', instance.username)\n instance.email = validated_data.get('email', instance.email)\n instance.save()\n\n profile.is_premium_member = profile_data.get(\n 'is_premium_member',\n profile.is_premium_member\n )\n profile.has_support_contract = profile_data.get(\n 'has_support_contract',\n profile.has_support_contract\n )\n profile.save()\n\n return instance\n\n\n\nBecause the behavior of nested creates and updates can be ambiguous, and may require complex dependencies between related models, REST framework 3 requires you to always write these methods explicitly. The default \nModelSerializer\n \n.create()\n and \n.update()\n methods do not include support for writable nested representations.\n\n\nThere are however, third-party packages available such as \nDRF Writable Nested\n that support automatic writable nested representations.\n\n\nHandling saving related instances in model manager classes\n\n\nAn alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances.\n\n\nFor example, suppose we wanted to ensure that \nUser\n instances and \nProfile\n instances are always created together as a pair. We might write a custom manager class that looks something like this:\n\n\nclass UserManager(models.Manager):\n ...\n\n def create(self, username, email, is_premium_member=False, has_support_contract=False):\n user = User(username=username, email=email)\n user.save()\n profile = Profile(\n user=user,\n is_premium_member=is_premium_member,\n has_support_contract=has_support_contract\n )\n profile.save()\n return user\n\n\n\nThis manager class now more nicely encapsulates that user instances and profile instances are always created at the same time. Our \n.create()\n method on the serializer class can now be re-written to use the new manager method.\n\n\ndef create(self, validated_data):\n return User.objects.create(\n username=validated_data['username'],\n email=validated_data['email']\n is_premium_member=validated_data['profile']['is_premium_member']\n has_support_contract=validated_data['profile']['has_support_contract']\n )\n\n\n\nFor more details on this approach see the Django documentation on \nmodel managers\n, and \nthis blogpost on using model and manager classes\n.\n\n\nDealing with multiple objects\n\n\nThe \nSerializer\n class can also handle serializing or deserializing lists of objects.\n\n\nSerializing multiple objects\n\n\nTo serialize a queryset or list of objects instead of a single object instance, you should pass the \nmany=True\n flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.\n\n\nqueryset = Book.objects.all()\nserializer = BookSerializer(queryset, many=True)\nserializer.data\n# [\n# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},\n# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},\n# {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}\n# ]\n\n\n\nDeserializing multiple objects\n\n\nThe default behavior for deserializing multiple objects is to support multiple object creation, but not support multiple object updates. For more information on how to support or customize either of these cases, see the \nListSerializer\n documentation below.\n\n\nIncluding extra context\n\n\nThere are some cases where you need to provide extra context to the serializer in addition to the object being serialized. One common case is if you're using a serializer that includes hyperlinked relations, which requires the serializer to have access to the current request so that it can properly generate fully qualified URLs.\n\n\nYou can provide arbitrary additional context by passing a \ncontext\n argument when instantiating the serializer. For example:\n\n\nserializer = AccountSerializer(account, context={'request': request})\nserializer.data\n# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}\n\n\n\nThe context dictionary can be used within any serializer field logic, such as a custom \n.to_representation()\n method, by accessing the \nself.context\n attribute.\n\n\n\n\nModelSerializer\n\n\nOften you'll want serializer classes that map closely to Django model definitions.\n\n\nThe \nModelSerializer\n class provides a shortcut that lets you automatically create a \nSerializer\n class with fields that correspond to the Model fields.\n\n\nThe \nModelSerializer\n class is the same as a regular \nSerializer\n class, except that\n:\n\n\n\n\nIt will automatically generate a set of fields for you, based on the model.\n\n\nIt will automatically generate validators for the serializer, such as unique_together validators.\n\n\nIt includes simple default implementations of \n.create()\n and \n.update()\n.\n\n\n\n\nDeclaring a \nModelSerializer\n looks like this:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n\n\n\nBy default, all the model fields on the class will be mapped to a corresponding serializer fields.\n\n\nAny relationships such as foreign keys on the model will be mapped to \nPrimaryKeyRelatedField\n. Reverse relationships are not included by default unless explicitly included as specified in the \nserializer relations\n documentation.\n\n\nInspecting a \nModelSerializer\n\n\nSerializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with \nModelSerializers\n where you want to determine what set of fields and validators are being automatically created for you.\n\n\nTo do so, open the Django shell, using \npython manage.py shell\n, then import the serializer class, instantiate it, and print the object representation\u2026\n\n\n>>> from myapp.serializers import AccountSerializer\n>>> serializer = AccountSerializer()\n>>> print(repr(serializer))\nAccountSerializer():\n id = IntegerField(label='ID', read_only=True)\n name = CharField(allow_blank=True, max_length=100, required=False)\n owner = PrimaryKeyRelatedField(queryset=User.objects.all())\n\n\n\nSpecifying which fields to include\n\n\nIf you only want a subset of the default fields to be used in a model serializer, you can do so using \nfields\n or \nexclude\n options, just as you would with a \nModelForm\n. It is strongly recommended that you explicitly set all fields that should be serialized using the \nfields\n attribute. This will make it less likely to result in unintentionally exposing data when your models change.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n\n\n\nYou can also set the \nfields\n attribute to the special value \n'__all__'\n to indicate that all fields in the model should be used.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = '__all__'\n\n\n\nYou can set the \nexclude\n attribute to a list of fields to be excluded from the serializer.\n\n\nFor example:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n exclude = ('users',)\n\n\n\nIn the example above, if the \nAccount\n model had 3 fields \naccount_name\n, \nusers\n, and \ncreated\n, this will result in the fields \naccount_name\n and \ncreated\n to be serialized.\n\n\nThe names in the \nfields\n and \nexclude\n attributes will normally map to model fields on the model class.\n\n\nAlternatively names in the \nfields\n options can map to properties or methods which take no arguments that exist on the model class.\n\n\nSince version 3.3.0, it is \nmandatory\n to provide one of the attributes \nfields\n or \nexclude\n.\n\n\nSpecifying nested serialization\n\n\nThe default \nModelSerializer\n uses primary keys for relationships, but you can also easily generate nested representations using the \ndepth\n option:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n depth = 1\n\n\n\nThe \ndepth\n option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.\n\n\nIf you want to customize the way the serialization is done you'll need to define the field yourself.\n\n\nSpecifying fields explicitly\n\n\nYou can add extra fields to a \nModelSerializer\n or override the default fields by declaring fields on the class, just as you would for a \nSerializer\n class.\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n url = serializers.CharField(source='get_absolute_url', read_only=True)\n groups = serializers.PrimaryKeyRelatedField(many=True)\n\n class Meta:\n model = Account\n\n\n\nExtra fields can correspond to any property or callable on the model.\n\n\nSpecifying read only fields\n\n\nYou may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the \nread_only=True\n attribute, you may use the shortcut Meta option, \nread_only_fields\n.\n\n\nThis option should be a list or tuple of field names, and is declared as follows:\n\n\nclass AccountSerializer(serializers.ModelSerializer):\n class Meta:\n model = Account\n fields = ('id', 'account_name', 'users', 'created')\n read_only_fields = ('account_name',)\n\n\n\nModel fields which have \neditable=False\n set, and \nAutoField\n fields will be set to read-only by default, and do not need to be added to the \nread_only_fields\n option.\n\n\n\n\nNote\n: There is a special-case where a read-only field is part of a \nunique_together\n constraint at the model level. In this case the field is required by the serializer class in order to validate the constraint, but should also not be editable by the user.\n\n\nThe right way to deal with this is to specify the field explicitly on the serializer, providing both the \nread_only=True\n and \ndefault=\u2026\n keyword arguments.\n\n\nOne example of this is a read-only relation to the currently authenticated \nUser\n which is \nunique_together\n with another identifier. In this case you would declare the user field like so:\n\n\nuser = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())\n\n\n\nPlease review the \nValidators Documentation\n for details on the \nUniqueTogetherValidator\n and \nCurrentUserDefault\n classes.\n\n\n\n\nAdditional keyword arguments\n\n\nThere is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the \nextra_kwargs\n option. As in the case of \nread_only_fields\n, this means you do not need to explicitly declare the field on the serializer.\n\n\nThis option is a dictionary, mapping field names to a dictionary of keyword arguments. For example:\n\n\nclass CreateUserSerializer(serializers.ModelSerializer):\n class Meta:\n model = User\n fields = ('email', 'username', 'password')\n extra_kwargs = {'password': {'write_only': True}}\n\n def create(self, validated_data):\n user = User(\n email=validated_data['email'],\n username=validated_data['username']\n )\n user.set_password(validated_data['password'])\n user.save()\n return user\n\n\n\nRelational fields\n\n\nWhen serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for \nModelSerializer\n is to use the primary keys of the related instances.\n\n\nAlternative representations include serializing using hyperlinks, serializing complete nested representations, or serializing with a custom representation.\n\n\nFor full details see the \nserializer relations\n documentation.\n\n\nCustomizing field mappings\n\n\nThe ModelSerializer class also exposes an API that you can override in order to alter how serializer fields are automatically determined when instantiating the serializer.\n\n\nNormally if a \nModelSerializer\n does not generate the fields you need by default then you should either add them to the class explicitly, or simply use a regular \nSerializer\n class instead. However in some cases you may want to create a new base class that defines how the serializer fields are created for any given model.\n\n\n.serializer_field_mapping\n\n\nA mapping of Django model classes to REST framework serializer classes. You can override this mapping to alter the default serializer classes that should be used for each model class.\n\n\n.serializer_related_field\n\n\nThis property should be the serializer field class, that is used for relational fields by default.\n\n\nFor \nModelSerializer\n this defaults to \nPrimaryKeyRelatedField\n.\n\n\nFor \nHyperlinkedModelSerializer\n this defaults to \nserializers.HyperlinkedRelatedField\n.\n\n\nserializer_url_field\n\n\nThe serializer field class that should be used for any \nurl\n field on the serializer.\n\n\nDefaults to \nserializers.HyperlinkedIdentityField\n\n\nserializer_choice_field\n\n\nThe serializer field class that should be used for any choice fields on the serializer.\n\n\nDefaults to \nserializers.ChoiceField\n\n\nThe field_class and field_kwargs API\n\n\nThe following methods are called to determine the class and keyword arguments for each field that should be automatically included on the serializer. Each of these methods should return a two tuple of \n(field_class, field_kwargs)\n.\n\n\n.build_standard_field(self, field_name, model_field)\n\n\nCalled to generate a serializer field that maps to a standard model field.\n\n\nThe default implementation returns a serializer class based on the \nserializer_field_mapping\n attribute.\n\n\n.build_relational_field(self, field_name, relation_info)\n\n\nCalled to generate a serializer field that maps to a relational model field.\n\n\nThe default implementation returns a serializer class based on the \nserializer_relational_field\n attribute.\n\n\nThe \nrelation_info\n argument is a named tuple, that contains \nmodel_field\n, \nrelated_model\n, \nto_many\n and \nhas_through_model\n properties.\n\n\n.build_nested_field(self, field_name, relation_info, nested_depth)\n\n\nCalled to generate a serializer field that maps to a relational model field, when the \ndepth\n option has been set.\n\n\nThe default implementation dynamically creates a nested serializer class based on either \nModelSerializer\n or \nHyperlinkedModelSerializer\n.\n\n\nThe \nnested_depth\n will be the value of the \ndepth\n option, minus one.\n\n\nThe \nrelation_info\n argument is a named tuple, that contains \nmodel_field\n, \nrelated_model\n, \nto_many\n and \nhas_through_model\n properties.\n\n\n.build_property_field(self, field_name, model_class)\n\n\nCalled to generate a serializer field that maps to a property or zero-argument method on the model class.\n\n\nThe default implementation returns a \nReadOnlyField\n class.\n\n\n.build_url_field(self, field_name, model_class)\n\n\nCalled to generate a serializer field for the serializer's own \nurl\n field. The default implementation returns a \nHyperlinkedIdentityField\n class.\n\n\n.build_unknown_field(self, field_name, model_class)\n\n\nCalled when the field name did not map to any model field or model property.\nThe default implementation raises an error, although subclasses may customize this behavior.\n\n\n\n\nHyperlinkedModelSerializer\n\n\nThe \nHyperlinkedModelSerializer\n class is similar to the \nModelSerializer\n class except that it uses hyperlinks to represent relationships, rather than primary keys.\n\n\nBy default the serializer will include a \nurl\n field instead of a primary key field.\n\n\nThe url field will be represented using a \nHyperlinkedIdentityField\n serializer field, and any relationships on the model will be represented using a \nHyperlinkedRelatedField\n serializer field.\n\n\nYou can explicitly include the primary key by adding it to the \nfields\n option, for example:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Account\n fields = ('url', 'id', 'account_name', 'users', 'created')\n\n\n\nAbsolute and relative URLs\n\n\nWhen instantiating a \nHyperlinkedModelSerializer\n you must include the current\n\nrequest\n in the serializer context, for example:\n\n\nserializer = AccountSerializer(queryset, context={'request': request})\n\n\n\nDoing so will ensure that the hyperlinks can include an appropriate hostname,\nso that the resulting representation uses fully qualified URLs, such as:\n\n\nhttp://api.example.com/accounts/1/\n\n\n\nRather than relative URLs, such as:\n\n\n/accounts/1/\n\n\n\nIf you \ndo\n want to use relative URLs, you should explicitly pass \n{'request': None}\n\nin the serializer context.\n\n\nHow hyperlinked views are determined\n\n\nThere needs to be a way of determining which views should be used for hyperlinking to model instances.\n\n\nBy default hyperlinks are expected to correspond to a view name that matches the style \n'{model_name}-detail'\n, and looks up the instance by a \npk\n keyword argument.\n\n\nYou can override a URL field view name and lookup field by using either, or both of, the \nview_name\n and \nlookup_field\n options in the \nextra_kwargs\n setting, like so:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n class Meta:\n model = Account\n fields = ('account_url', 'account_name', 'users', 'created')\n extra_kwargs = {\n 'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},\n 'users': {'lookup_field': 'username'}\n }\n\n\n\nAlternatively you can set the fields on the serializer explicitly. For example:\n\n\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n url = serializers.HyperlinkedIdentityField(\n view_name='accounts',\n lookup_field='slug'\n )\n users = serializers.HyperlinkedRelatedField(\n view_name='user-detail',\n lookup_field='username',\n many=True,\n read_only=True\n )\n\n class Meta:\n model = Account\n fields = ('url', 'account_name', 'users', 'created')\n\n\n\n\n\nTip\n: Properly matching together hyperlinked representations and your URL conf can sometimes be a bit fiddly. Printing the \nrepr\n of a \nHyperlinkedModelSerializer\n instance is a particularly useful way to inspect exactly which view names and lookup fields the relationships are expected to map too.\n\n\n\n\nChanging the URL field name\n\n\nThe name of the URL field defaults to 'url'. You can override this globally, by using the \nURL_FIELD_NAME\n setting.\n\n\n\n\nListSerializer\n\n\nThe \nListSerializer\n class provides the behavior for serializing and validating multiple objects at once. You won't \ntypically\n need to use \nListSerializer\n directly, but should instead simply pass \nmany=True\n when instantiating a serializer.\n\n\nWhen a serializer is instantiated and \nmany=True\n is passed, a \nListSerializer\n instance will be created. The serializer class then becomes a child of the parent \nListSerializer\n\n\nThe following argument can also be passed to a \nListSerializer\n field or a serializer that is passed \nmany=True\n:\n\n\nallow_empty\n\n\nThis is \nTrue\n by default, but can be set to \nFalse\n if you want to disallow empty lists as valid input.\n\n\nCustomizing \nListSerializer\n behavior\n\n\nThere \nare\n a few use cases when you might want to customize the \nListSerializer\n behavior. For example:\n\n\n\n\nYou want to provide particular validation of the lists, such as checking that one element does not conflict with another element in a list.\n\n\nYou want to customize the create or update behavior of multiple objects.\n\n\n\n\nFor these cases you can modify the class that is used when \nmany=True\n is passed, by using the \nlist_serializer_class\n option on the serializer \nMeta\n class.\n\n\nFor example:\n\n\nclass CustomListSerializer(serializers.ListSerializer):\n ...\n\nclass CustomSerializer(serializers.Serializer):\n ...\n class Meta:\n list_serializer_class = CustomListSerializer\n\n\n\nCustomizing multiple create\n\n\nThe default implementation for multiple object creation is to simply call \n.create()\n for each item in the list. If you want to customize this behavior, you'll need to customize the \n.create()\n method on \nListSerializer\n class that is used when \nmany=True\n is passed.\n\n\nFor example:\n\n\nclass BookListSerializer(serializers.ListSerializer):\n def create(self, validated_data):\n books = [Book(**item) for item in validated_data]\n return Book.objects.bulk_create(books)\n\nclass BookSerializer(serializers.Serializer):\n ...\n class Meta:\n list_serializer_class = BookListSerializer\n\n\n\nCustomizing multiple update\n\n\nBy default the \nListSerializer\n class does not support multiple updates. This is because the behavior that should be expected for insertions and deletions is ambiguous.\n\n\nTo support multiple updates you'll need to do so explicitly. When writing your multiple update code make sure to keep the following in mind:\n\n\n\n\nHow do you determine which instance should be updated for each item in the list of data?\n\n\nHow should insertions be handled? Are they invalid, or do they create new objects?\n\n\nHow should removals be handled? Do they imply object deletion, or removing a relationship? Should they be silently ignored, or are they invalid?\n\n\nHow should ordering be handled? Does changing the position of two items imply any state change or is it ignored?\n\n\n\n\nYou will need to add an explicit \nid\n field to the instance serializer. The default implicitly-generated \nid\n field is marked as \nread_only\n. This causes it to be removed on updates. Once you declare it explicitly, it will be available in the list serializer's \nupdate\n method.\n\n\nHere's an example of how you might choose to implement multiple updates:\n\n\nclass BookListSerializer(serializers.ListSerializer):\n def update(self, instance, validated_data):\n # Maps for id->instance and id->data item.\n book_mapping = {book.id: book for book in instance}\n data_mapping = {item['id']: item for item in validated_data}\n\n # Perform creations and updates.\n ret = []\n for book_id, data in data_mapping.items():\n book = book_mapping.get(book_id, None)\n if book is None:\n ret.append(self.child.create(data))\n else:\n ret.append(self.child.update(book, data))\n\n # Perform deletions.\n for book_id, book in book_mapping.items():\n if book_id not in data_mapping:\n book.delete()\n\n return ret\n\nclass BookSerializer(serializers.Serializer):\n # We need to identify elements in the list using their primary key,\n # so use a writable field here, rather than the default which would be read-only.\n id = serializers.IntegerField()\n ...\n\n class Meta:\n list_serializer_class = BookListSerializer\n\n\n\nIt is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the \nallow_add_remove\n behavior that was present in REST framework 2.\n\n\nCustomizing ListSerializer initialization\n\n\nWhen a serializer with \nmany=True\n is instantiated, we need to determine which arguments and keyword arguments should be passed to the \n.__init__()\n method for both the child \nSerializer\n class, and for the parent \nListSerializer\n class.\n\n\nThe default implementation is to pass all arguments to both classes, except for \nvalidators\n, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.\n\n\nOccasionally you might need to explicitly specify how the child and parent classes should be instantiated when \nmany=True\n is passed. You can do so by using the \nmany_init\n class method.\n\n\n @classmethod\n def many_init(cls, *args, **kwargs):\n # Instantiate the child serializer.\n kwargs['child'] = cls()\n # Instantiate the parent list serializer.\n return CustomListSerializer(*args, **kwargs)\n\n\n\n\n\nBaseSerializer\n\n\nBaseSerializer\n class that can be used to easily support alternative serialization and deserialization styles.\n\n\nThis class implements the same basic API as the \nSerializer\n class:\n\n\n\n\n.data\n - Returns the outgoing primitive representation.\n\n\n.is_valid()\n - Deserializes and validates incoming data.\n\n\n.validated_data\n - Returns the validated incoming data.\n\n\n.errors\n - Returns any errors during validation.\n\n\n.save()\n - Persists the validated data into an object instance.\n\n\n\n\nThere are four methods that can be overridden, depending on what functionality you want the serializer class to support:\n\n\n\n\n.to_representation()\n - Override this to support serialization, for read operations.\n\n\n.to_internal_value()\n - Override this to support deserialization, for write operations.\n\n\n.create()\n and \n.update()\n - Override either or both of these to support saving instances.\n\n\n\n\nBecause this class provides the same interface as the \nSerializer\n class, you can use it with the existing generic class-based views exactly as you would for a regular \nSerializer\n or \nModelSerializer\n.\n\n\nThe only difference you'll notice when doing so is the \nBaseSerializer\n classes will not generate HTML forms in the browsable API. This is because the data they return does not include all the field information that would allow each field to be rendered into a suitable HTML input.\n\n\nRead-only \nBaseSerializer\n classes\n\n\nTo implement a read-only serializer using the \nBaseSerializer\n class, we just need to override the \n.to_representation()\n method. Let's take a look at an example using a simple Django model:\n\n\nclass HighScore(models.Model):\n created = models.DateTimeField(auto_now_add=True)\n player_name = models.CharField(max_length=10)\n score = models.IntegerField()\n\n\n\nIt's simple to create a read-only serializer for converting \nHighScore\n instances into primitive data types.\n\n\nclass HighScoreSerializer(serializers.BaseSerializer):\n def to_representation(self, obj):\n return {\n 'score': obj.score,\n 'player_name': obj.player_name\n }\n\n\n\nWe can now use this class to serialize single \nHighScore\n instances:\n\n\n@api_view(['GET'])\ndef high_score(request, pk):\n instance = HighScore.objects.get(pk=pk)\n serializer = HighScoreSerializer(instance)\n return Response(serializer.data)\n\n\n\nOr use it to serialize multiple instances:\n\n\n@api_view(['GET'])\ndef all_high_scores(request):\n queryset = HighScore.objects.order_by('-score')\n serializer = HighScoreSerializer(queryset, many=True)\n return Response(serializer.data)\n\n\n\nRead-write \nBaseSerializer\n classes\n\n\nTo create a read-write serializer we first need to implement a \n.to_internal_value()\n method. This method returns the validated values that will be used to construct the object instance, and may raise a \nValidationError\n if the supplied data is in an incorrect format.\n\n\nOnce you've implemented \n.to_internal_value()\n, the basic validation API will be available on the serializer, and you will be able to use \n.is_valid()\n, \n.validated_data\n and \n.errors\n.\n\n\nIf you want to also support \n.save()\n you'll need to also implement either or both of the \n.create()\n and \n.update()\n methods.\n\n\nHere's a complete example of our previous \nHighScoreSerializer\n, that's been updated to support both read and write operations.\n\n\nclass HighScoreSerializer(serializers.BaseSerializer):\n def to_internal_value(self, data):\n score = data.get('score')\n player_name = data.get('player_name')\n\n # Perform the data validation.\n if not score:\n raise ValidationError({\n 'score': 'This field is required.'\n })\n if not player_name:\n raise ValidationError({\n 'player_name': 'This field is required.'\n })\n if len(player_name) > 10:\n raise ValidationError({\n 'player_name': 'May not be more than 10 characters.'\n })\n\n # Return the validated values. This will be available as\n # the `.validated_data` property.\n return {\n 'score': int(score),\n 'player_name': player_name\n }\n\n def to_representation(self, obj):\n return {\n 'score': obj.score,\n 'player_name': obj.player_name\n }\n\n def create(self, validated_data):\n return HighScore.objects.create(**validated_data)\n\n\n\nCreating new base classes\n\n\nThe \nBaseSerializer\n class is also useful if you want to implement new generic serializer classes for dealing with particular serialization styles, or for integrating with alternative storage backends.\n\n\nThe following class is an example of a generic serializer that can handle coercing arbitrary objects into primitive representations.\n\n\nclass ObjectSerializer(serializers.BaseSerializer):\n \"\"\"\n A read-only serializer that coerces arbitrary complex objects\n into primitive representations.\n \"\"\"\n def to_representation(self, obj):\n for attribute_name in dir(obj):\n attribute = getattr(obj, attribute_name)\n if attribute_name('_'):\n # Ignore private attributes.\n pass\n elif hasattr(attribute, '__call__'):\n # Ignore methods and other callables.\n pass\n elif isinstance(attribute, (str, int, bool, float, type(None))):\n # Primitive types can be passed through unmodified.\n output[attribute_name] = attribute\n elif isinstance(attribute, list):\n # Recursively deal with items in lists.\n output[attribute_name] = [\n self.to_representation(item) for item in attribute\n ]\n elif isinstance(attribute, dict):\n # Recursively deal with items in dictionaries.\n output[attribute_name] = {\n str(key): self.to_representation(value)\n for key, value in attribute.items()\n }\n else:\n # Force anything else to its string representation.\n output[attribute_name] = str(attribute)\n\n\n\n\n\nAdvanced serializer usage\n\n\nOverriding serialization and deserialization behavior\n\n\nIf you need to alter the serialization or deserialization behavior of a serializer class, you can do so by overriding the \n.to_representation()\n or \n.to_internal_value()\n methods.\n\n\nSome reasons this might be useful include...\n\n\n\n\nAdding new behavior for new serializer base classes.\n\n\nModifying the behavior slightly for an existing class.\n\n\nImproving serialization performance for a frequently accessed API endpoint that returns lots of data.\n\n\n\n\nThe signatures for these methods are as follows:\n\n\n.to_representation(self, obj)\n\n\nTakes the object instance that requires serialization, and should return a primitive representation. Typically this means returning a structure of built-in Python datatypes. The exact types that can be handled will depend on the render classes you have configured for your API.\n\n\nMay be overridden in order modify the representation style. For example:\n\n\ndef to_representation(self, instance):\n \"\"\"Convert `username` to lowercase.\"\"\"\n ret = super().to_representation(instance)\n ret['username'] = ret['username'].lower()\n return ret\n\n\n\n.to_internal_value(self, data)\n\n\nTakes the unvalidated incoming data as input and should return the validated data that will be made available as \nserializer.validated_data\n. The return value will also be passed to the \n.create()\n or \n.update()\n methods if \n.save()\n is called on the serializer class.\n\n\nIf any of the validation fails, then the method should raise a \nserializers.ValidationError(errors)\n. The \nerrors\n argument should be a dictionary mapping field names (or \nsettings.NON_FIELD_ERRORS_KEY\n) to a list of error messages. If you don't need to alter deserialization behavior and instead want to provide object-level validation, it's recommended that you instead override the \n.validate()\n method.\n\n\nThe \ndata\n argument passed to this method will normally be the value of \nrequest.data\n, so the datatype it provides will depend on the parser classes you have configured for your API.\n\n\nSerializer Inheritance\n\n\nSimilar to Django forms, you can extend and reuse serializers through inheritance. This allows you to declare a common set of fields or methods on a parent class that can then be used in a number of serializers. For example,\n\n\nclass MyBaseSerializer(Serializer):\n my_field = serializers.CharField()\n\n def validate_my_field(self):\n ...\n\nclass MySerializer(MyBaseSerializer):\n ...\n\n\n\nLike Django's \nModel\n and \nModelForm\n classes, the inner \nMeta\n class on serializers does not implicitly inherit from it's parents' inner \nMeta\n classes. If you want the \nMeta\n class to inherit from a parent class you must do so explicitly. For example:\n\n\nclass AccountSerializer(MyBaseSerializer):\n class Meta(MyBaseSerializer.Meta):\n model = Account\n\n\n\nTypically we would recommend \nnot\n using inheritance on inner Meta classes, but instead declaring all options explicitly.\n\n\nAdditionally, the following caveats apply to serializer inheritance:\n\n\n\n\nNormal Python name resolution rules apply. If you have multiple base classes that declare a \nMeta\n inner class, only the first one will be used. This means the child\u2019s \nMeta\n, if it exists, otherwise the \nMeta\n of the first parent, etc.\n\n\n\n\nIt\u2019s possible to declaratively remove a \nField\n inherited from a parent class by setting the name to be \nNone\n on the subclass.\n\n\nclass MyBaseSerializer(ModelSerializer):\n my_field = serializers.CharField()\n\nclass MySerializer(MyBaseSerializer):\n my_field = None\n\n\n\nHowever, you can only use this technique to opt out from a field defined declaratively by a parent class; it won\u2019t prevent the \nModelSerializer\n from generating a default field. To opt-out from default fields, see \nSpecifying which fields to include\n.\n\n\n\n\n\n\nDynamically modifying fields\n\n\nOnce a serializer has been initialized, the dictionary of fields that are set on the serializer may be accessed using the \n.fields\n attribute. Accessing and modifying this attribute allows you to dynamically modify the serializer.\n\n\nModifying the \nfields\n argument directly allows you to do interesting things such as changing the arguments on serializer fields at runtime, rather than at the point of declaring the serializer.\n\n\nExample\n\n\nFor example, if you wanted to be able to set which fields should be used by a serializer at the point of initializing it, you could create a serializer class like so:\n\n\nclass DynamicFieldsModelSerializer(serializers.ModelSerializer):\n \"\"\"\n A ModelSerializer that takes an additional `fields` argument that\n controls which fields should be displayed.\n \"\"\"\n\n def __init__(self, *args, **kwargs):\n # Don't pass the 'fields' arg up to the superclass\n fields = kwargs.pop('fields', None)\n\n # Instantiate the superclass normally\n super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)\n\n if fields is not None:\n # Drop any fields that are not specified in the `fields` argument.\n allowed = set(fields)\n existing = set(self.fields)\n for field_name in existing - allowed:\n self.fields.pop(field_name)\n\n\n\nThis would then allow you to do the following:\n\n\n>>> class UserSerializer(DynamicFieldsModelSerializer):\n>>> class Meta:\n>>> model = User\n>>> fields = ('id', 'username', 'email')\n>>>\n>>> print UserSerializer(user)\n{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}\n>>>\n>>> print UserSerializer(user, fields=('id', 'email'))\n{'id': 2, 'email': 'jon@example.com'}\n\n\n\nCustomizing the default fields\n\n\nREST framework 2 provided an API to allow developers to override how a \nModelSerializer\n class would automatically generate the default set of fields.\n\n\nThis API included the \n.get_field()\n, \n.get_pk_field()\n and other methods.\n\n\nBecause the serializers have been fundamentally redesigned with 3.0 this API no longer exists. You can still modify the fields that get created but you'll need to refer to the source code, and be aware that if the changes you make are against private bits of API then they may be subject to change.\n\n\n\n\nThird party packages\n\n\nThe following third party packages are also available.\n\n\nDjango REST marshmallow\n\n\nThe \ndjango-rest-marshmallow\n package provides an alternative implementation for serializers, using the python \nmarshmallow\n library. It exposes the same API as the REST framework serializers, and can be used as a drop-in replacement in some use-cases.\n\n\nSerpy\n\n\nThe \nserpy\n package is an alternative implementation for serializers that is built for speed. \nSerpy\n serializes complex datatypes to simple native types. The native types can be easily converted to JSON or any other format needed.\n\n\nMongoengineModelSerializer\n\n\nThe \ndjango-rest-framework-mongoengine\n package provides a \nMongoEngineModelSerializer\n serializer class that supports using MongoDB as the storage layer for Django REST framework.\n\n\nGeoFeatureModelSerializer\n\n\nThe \ndjango-rest-framework-gis\n package provides a \nGeoFeatureModelSerializer\n serializer class that supports GeoJSON both for read and write operations.\n\n\nHStoreSerializer\n\n\nThe \ndjango-rest-framework-hstore\n package provides an \nHStoreSerializer\n to support \ndjango-hstore\n \nDictionaryField\n model field and its \nschema-mode\n feature.\n\n\nDynamic REST\n\n\nThe \ndynamic-rest\n package extends the ModelSerializer and ModelViewSet interfaces, adding API query parameters for filtering, sorting, and including / excluding all fields and relationships defined by your serializers.\n\n\nDynamic Fields Mixin\n\n\nThe \ndrf-dynamic-fields\n package provides a mixin to dynamically limit the fields per serializer to a subset specified by an URL parameter.\n\n\nDRF FlexFields\n\n\nThe \ndrf-flex-fields\n package extends the ModelSerializer and ModelViewSet to provide commonly used functionality for dynamically setting fields and expanding primitive fields to nested models, both from URL parameters and your serializer class definitions.\n\n\nSerializer Extensions\n\n\nThe \ndjango-rest-framework-serializer-extensions\n\npackage provides a collection of tools to DRY up your serializers, by allowing\nfields to be defined on a per-view/request basis. Fields can be whitelisted,\nblacklisted and child serializers can be optionally expanded.\n\n\nHTML JSON Forms\n\n\nThe \nhtml-json-forms\n package provides an algorithm and serializer for processing \n
diff --git a/topics/contributing/index.html b/topics/contributing/index.html
index e8ec49a01..280120068 100644
--- a/topics/contributing/index.html
+++ b/topics/contributing/index.html
@@ -490,9 +490,13 @@
To start developing on Django REST framework, clone the repo:
-git clone git@github.com:encode/django-rest-framework.git
+To start developing on Django REST framework, first create a Fork from the
+Django REST Framework repo on GitHub.
+Then clone your fork. The clone command will look like this, with your GitHub
+username instead of YOUR-USERNAME:
+git clone https://github.com/YOUR-USERNAME/Spoon-Knife
+See GitHub's Fork a Repo Guide for more help.
Changes should broadly follow the PEP 8 style conventions, and we recommend you set up your editor to automatically indicate non-conforming styles.
Testing
To run the tests, clone the repository, and then:
diff --git a/topics/documenting-your-api/index.html b/topics/documenting-your-api/index.html
index 9afa403cf..346365ef3 100644
--- a/topics/documenting-your-api/index.html
+++ b/topics/documenting-your-api/index.html
@@ -505,6 +505,7 @@ For example:
generator_class: Default rest_framework.schemas.SchemaGenerator. May be used to specify a SchemaGenerator subclass to be passed to the SchemaView.
authentication_classes: Default api_settings.DEFAULT_AUTHENTICATION_CLASSES. May be used to pass custom authentication classes to the SchemaView.
permission_classes: Default api_settings.DEFAULT_PERMISSION_CLASSES May be used to pass custom permission classes to the SchemaView.
+renderer_classes: Default None. May be used to pass custom renderer classes to the SchemaView.
get_docs_view
@@ -515,7 +516,8 @@ For example:
patterns: Default None. A list of URLs to inspect when generating the schema. If None project's URL conf will be used.
generator_class: Default rest_framework.schemas.SchemaGenerator. May be used to specify a SchemaGenerator subclass to be passed to the SchemaView.
authentication_classes: Default api_settings.DEFAULT_AUTHENTICATION_CLASSES. May be used to pass custom authentication classes to the SchemaView.
-permission_classes: Default api_settings.DEFAULT_PERMISSION_CLASSES May be used to pass custom permission classes to the SchemaView.
+permission_classes: Default api_settings.DEFAULT_PERMISSION_CLASSES. May be used to pass custom permission classes to the SchemaView.
+renderer_classes: Default None. May be used to pass custom renderer classes to the SchemaView. If None the SchemaView will be configured with DocumentationRenderer and CoreJSONRenderer renderers, corresponding to the (default) html and corejson formats.
get_schemajs_view
@@ -528,14 +530,28 @@ For example:
authentication_classes: Default api_settings.DEFAULT_AUTHENTICATION_CLASSES. May be used to pass custom authentication classes to the SchemaView.
permission_classes: Default api_settings.DEFAULT_PERMISSION_CLASSES May be used to pass custom permission classes to the SchemaView.
+Customising code samples
+The built-in API documentation includes automatically generated code samples for
+each of the available API client libraries.
+You may customise these samples by subclassing DocumentationRenderer, setting
+languages to the list of languages you wish to support:
+from rest_framework.renderers import DocumentationRenderer
+
+
+class CustomRenderer(DocumentationRenderer):
+ languages = ['ruby', 'go']
+
+For each language you need to provide an intro template, detailing installation instructions and such,
+plus a generic template for making API requests, that can be filled with individual request details.
+See the templates for the bundled languages for examples.
Third party packages
There are a number of mature third-party packages for providing API documentation.
drf-yasg - Yet Another Swagger Generator
-drf-yasg is a Swagger generation tool implemented without using the schema generation provided
+
drf-yasg is a Swagger generation tool implemented without using the schema generation provided
by Django Rest Framework.
-It aims to implement as much of the OpenAPI specification as possible - nested schemas, named models,
-response bodies, enum/pattern/min/max validators, form parameters, etc. - and to generate documents usable with code
+
It aims to implement as much of the OpenAPI specification as possible - nested schemas, named models,
+response bodies, enum/pattern/min/max validators, form parameters, etc. - and to generate documents usable with code
generation tools like swagger-codegen.
This also translates into a very useful interactive documentation viewer in the form of swagger-ui:

diff --git a/topics/funding/index.html b/topics/funding/index.html
index d08d51a96..c1b7e1b06 100644
--- a/topics/funding/index.html
+++ b/topics/funding/index.html
@@ -750,7 +750,7 @@ DRF is one of the core reasons why Django is top choice among web frameworks tod
For further enquires please contact funding@django-rest-framework.org.
Accountability
-In an effort to keep the project as transparent as possible, we are releasing monthly progress reports and regularly include financial reports and cost breakdowns.
+In an effort to keep the project as transparent as possible, we are releasing monthly progress reports and regularly include financial reports and cost breakdowns.
diff --git a/topics/jobs/index.html b/topics/jobs/index.html
index 5e9850d29..58049f05d 100644
--- a/topics/jobs/index.html
+++ b/topics/jobs/index.html
@@ -420,6 +420,7 @@
https://www.technojobs.co.uk/django-jobs
https://remoteok.io/remote-django-jobs
https://www.remotepython.com/jobs/
+https://weworkcontract.com/python-contract-jobs
Know of any other great resources for Django REST Framework jobs that are missing in our list? Please submit a pull request or email us.
Wonder how else you can help? One of the best ways you can help Django REST Framework is to ask interviewers if their company is signed up for REST Framework sponsorship yet.
diff --git a/topics/release-notes/index.html b/topics/release-notes/index.html
index 207cdd7b6..05ed639b1 100644
--- a/topics/release-notes/index.html
+++ b/topics/release-notes/index.html
@@ -398,6 +398,10 @@
Upgrading
+
+ 3.8.x series
+
+
3.7.x series
@@ -473,10 +477,116 @@
To upgrade Django REST framework to the latest version, use pip:
pip install -U djangorestframework
-You can determine your currently installed version using pip freeze:
-pip freeze | grep djangorestframework
+You can determine your currently installed version using pip show:
+pip show djangorestframework
+3.8.x series
+3.8.0
+Date: 3rd April 2018
+
+-
+
Breaking Change: Alter read_only plus default behaviour. #5886
+read_only fields will now always be excluded from writable fields.
+Previously read_only fields with a default value would use the default for create and update operations.
+In order to maintain the old behaviour you may need to pass the value of read_only fields when calling save() in
+the view:
+def perform_create(self, serializer):
+ serializer.save(owner=self.request.user)
+
+Alternatively you may override save() or create() or update() on the serialiser as appropriate.
+* Correct allow_null behaviour when required=False #5888
+Without an explicit default, allow_null implies a default of null for outgoing serialisation. Previously such
+fields were being skipped when read-only or otherwise not required.
+Possible backwards compatibility break if you were relying on such fields being excluded from the outgoing
+representation. In order to restore the old behaviour you can override data to exclude the field when None.
+For example:
+@property
+def data(self):
+ """
+ Drop `maybe_none` field if None.
+ """
+ data = super().data()
+ if 'maybe_none' in data and data['maybe_none'] is None:
+ del data['maybe_none']
+ return data
+
+
+-
+
Refactor dynamic route generation and improve viewset action introspectibility. #5705
+ViewSets have been provided with new attributes and methods that allow
+it to introspect its set of actions and the details of the current action.
+
+- Merged
list_route and detail_route into a single action decorator.
+- Get all extra actions on a
ViewSet with .get_extra_actions().
+- Extra actions now set the
url_name and url_path on the decorated method.
+- Enable action url reversing through
.reverse_action() method (added in 3.7.4)
+- Example reverse call:
self.reverse_action(self.custom_action.url_name)
+- Add
detail initkwarg to indicate if the current action is operating on a
+ collection or a single instance.
+
+Additional changes:
+
+- Deprecated
list_route & detail_route in favor of action decorator with detail boolean.
+- Deprecated dynamic list/detail route variants in favor of
DynamicRoute with detail boolean.
+- Refactored the router's dynamic route generation.
+- Fix formatting of the 3.7.4 release note #5704
+- Docs: Update DRF Writable Nested Serializers references #5711
+- Docs: Fixed typo in auth URLs example. #5713
+- Improve composite field child errors #5655
+- Disable HTML inputs for dict/list fields #5702
+- Fix typo in HostNameVersioning doc #5709
+- Use rsplit to get module and classname for imports #5712
+- Formalize URLPatternsTestCase #5703
+- Add exception translation test #5700
+- Test staticfiles #5701
+- Add drf-yasg to documentation and schema 3rd party packages #5720
+- Remove unused
compat._resolve_model() #5733
+- Drop compat workaround for unsupported Python 3.2 #5734
+- Prefer
iter(dict) over iter(dict.keys()) #5736
+- Pass
python_requires argument to setuptools #5739
+- Remove unused links from docs #5735
+- Prefer https protocol for links in docs when available #5729
+- Add HStoreField, postgres fields tests #5654
+- Always fully qualify ValidationError in docs #5751
+- Remove unreachable code from ManualSchema #5766
+- Allowed customising API documentation code samples #5752
+- Updated docs to use
pip show #5757
+- Load 'static' instead of 'staticfiles' in templates #5773
+- Fixed a typo in
fields docs #5783
+- Refer to "NamespaceVersioning" instead of "NamespacedVersioning" in the documentation #5754
+- ErrorDetail: add
__eq__/__ne__ and __repr__ #5787
+- Replace
background-attachment: fixed in docs #5777
+- Make 404 & 403 responses consistent with
exceptions.APIException output #5763
+- Small fix to API documentation: schemas #5796
+- Fix schema generation for PrimaryKeyRelatedField #5764
+- Represent serializer DictField as an Object in schema #5765
+- Added docs example reimplementing ObtainAuthToken #5802
+- Add schema to the ObtainAuthToken view #5676
+- Fix request formdata handling #5800
+- Fix authtoken views imports #5818
+- Update pytest, isort #5815 #5817 #5894
+- Fixed active timezone handling for non ISO8601 datetimes. #5833
+- Made TemplateHTMLRenderer render IntegerField inputs when value is
0. #5834
+- Corrected endpoint in tutorial instructions #5835
+- Add Django Rest Framework Role Filters to Third party packages #5809
+- Use single copy of static assets. Update jQuery #5823
+- Changes ternary conditionals to be PEP308 compliant #5827
+- Added links to 'A Todo List API with React' and 'Blog API' tutorials #5837
+- Fix comment typo in ModelSerializer #5844
+- Add admin to installed apps to avoid test failures. #5870
+- Fixed schema for UUIDField in SimpleMetadata. #5872
+- Corrected docs on router include with namespaces. #5843
+- Test using model objects for dotted source default #5880
+- Allow traversing nullable related fields #5849
+- Added: Tutorial: Django REST with React (Django 2.0) #5891
+- Add
LimitOffsetPagination.get_count to allow method override #5846
+- Don't show hidden fields in metadata #5854
+- Enable OrderingFilter to handle an empty tuple (or list) for the 'ordering' field. #5899
+- Added generic 500 and 400 JSON error handlers. #5904
+
+
+
3.7.x series
3.7.7
Date: 21st December 2017
@@ -1327,6 +1437,8 @@ Previously may have been stored internally as None.
+
+
diff --git a/topics/tutorials-and-resources/index.html b/topics/tutorials-and-resources/index.html
index fb55a758b..fa68024a2 100644
--- a/topics/tutorials-and-resources/index.html
+++ b/topics/tutorials-and-resources/index.html
@@ -445,6 +445,9 @@
Check Credentials Using Django REST Framework
Creating a Production Ready API with Python and Django REST Framework – Part 1
Creating a Production Ready API with Python and Django REST Framework – Part 2
+Django REST Framework Tutorial - Build a Blog API
+Django REST Framework & React Tutorial - Build a Todo List API
+Tutorial: Django REST with React (Django 2.0)
Videos
Talks
diff --git a/tutorial/4-authentication-and-permissions/index.html b/tutorial/4-authentication-and-permissions/index.html
index dcef4ebe4..c5ec7e255 100644
--- a/tutorial/4-authentication-and-permissions/index.html
+++ b/tutorial/4-authentication-and-permissions/index.html
@@ -465,8 +465,8 @@ from pygments import highlight
representation of the code snippet.
"""
lexer = get_lexer_by_name(self.language)
- linenos = self.linenos and 'table' or False
- options = self.title and {'title': self.title} or {}
+ linenos = 'table' if self.linenos else False
+ options = {'title': self.title} if self.title else {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
diff --git a/tutorial/6-viewsets-and-routers/index.html b/tutorial/6-viewsets-and-routers/index.html
index b7065521a..3c1e069e4 100644
--- a/tutorial/6-viewsets-and-routers/index.html
+++ b/tutorial/6-viewsets-and-routers/index.html
@@ -435,7 +435,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
Here we've used the ReadOnlyModelViewSet class to automatically provide the default 'read-only' operations. We're still setting the queryset and serializer_class attributes exactly as we did when we were using regular views, but we no longer need to provide the same information to two separate classes.
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):
@@ -450,7 +450,7 @@ class SnippetViewSet(viewsets.ModelViewSet):
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)
@@ -459,9 +459,9 @@ class SnippetViewSet(viewsets.ModelViewSet):
serializer.save(owner=self.request.user)
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.
-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.
-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.
+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 @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.
Binding ViewSets to URLs explicitly
The handler methods only get bound to the actions when we define the URLConf.
To see what's going on under the hood let's first explicitly create a set of views from our ViewSets.
diff --git a/tutorial/7-schemas-and-client-libraries/index.html b/tutorial/7-schemas-and-client-libraries/index.html
index de1a0f101..684e9c7d5 100644
--- a/tutorial/7-schemas-and-client-libraries/index.html
+++ b/tutorial/7-schemas-and-client-libraries/index.html
@@ -461,7 +461,7 @@ urlpatterns = [
]
-If you visit the API root endpoint in a browser you should now see corejson
+
If you visit the /schema/ endpoint in a browser you should now see corejson
representation become available as an option.

We can also request the schema from the command line, by specifying the desired