REST framework’s Request class extends the standard HttpRequest, adding support for REST framework’s flexible request parsing and request authentication.
request.data returns the parsed content of the request body.request.query_params is for getting the query parameters.request.user typically returns an instance of django.contrib.auth.models.User, although the behavior depends on the authentication policy being used.request.auth returns any additional authentication context.request.method returns the uppercased string representation of the request’s HTTP method.request.content_type, returns a string object representing the media type of the HTTP request’s body, or an empty string if no media type was provided.Response class which allows you to return content that can be rendered into multiple content types, depending on the client request.Response class subclasses Django’s SimpleTemplateResponse.Response class, you can also return regular HttpResponse or StreamingHttpResponse objects from your views if required.Response class simply provides a nicer interface for returning content-negotiated Web API responses, that can be rendered to multiple formats.Signature: Response(data, status=None, template_name=None, headers=None, content_type=None)
data: The serialized data for the response.
status: A status code for the response. Defaults to 200. See also status codes.
template_name: A template name to use if HTMLRenderer is selected.
headers: A dictionary of HTTP headers to use in the response.
content_type: The content type of the response. Typically, this will be set automatically by the renderer as determined by content negotiation, but there may be some cases where you need to specify the content type explicitly.
Unlike regular HttpResponse objects, you do not instantiate Response objects with rendered content. Instead you pass in unrendered data, which may consist of any Python primitives.
Note: The renderers used by the Response class cannot natively handle complex datatypes such as Django model instances, so you need to serialize the data into primitive datatypes before creating the Response object.
Using bare status codes in your responses isn’t recommended. REST framework includes a set of named constants that you can use to make your code more obvious and readable.
from rest_framework import status
from rest_framework.response import Response
def empty_view(self):
content = {'please move along': 'nothing to see here'}
return Response(content, status=status.HTTP_404_NOT_FOUND)
The full set of HTTP status codes included in the status module is listed below.
The module also includes a set of helper functions for testing if a status code is in a given range.
from rest_framework import status
from rest_framework.test import APITestCase
class ExampleTestCase(APITestCase):
def test_url_root(self):
url = reverse('index')
response = self.client.get(url)
self.assertTrue(status.is_success(response.status_code))
This class of status code indicates a provisional response. There are no 1xx status codes used in REST framework by default.
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
This class of status code indicates that the client’s request was successfully received, understood, and accepted.
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
HTTP_208_ALREADY_REPORTED
HTTP_226_IM_USED
This class of status code indicates that further action needs to be taken by the user agent in order to fulfill the request.
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
HTTP_308_PERMANENT_REDIRECT
The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition.
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_426_UPGRADE_REQUIRED
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
Response status codes beginning with the digit “5” indicate cases in which the server is aware that it has erred or is incapable of performing the request. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition.
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_506_VARIANT_ALSO_NEGOTIATES
HTTP_507_INSUFFICIENT_STORAGE
HTTP_508_LOOP_DETECTED
HTTP_509_BANDWIDTH_LIMIT_EXCEEDED
HTTP_510_NOT_EXTENDED
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
The following helper functions are available for identifying the category of the response code.
is_informational() # 1xx
is_success() # 2xx
is_redirect() # 3xx
is_client_error() # 4xx
is_server_error() # 5xx
Note: Refer to this website for a nice browsable documentation on views.
REST framework provides an APIView class, which subclasses Django’s View class.
APIView classes are different from regular View classes in the following ways:
Request instances, not Django’s HttpRequest instances.Response, instead of Django’s HttpResponse. The view will manage content negotiation and setting the correct renderer on the response.APIException exceptions will be caught and mediated into appropriate responses.Using the APIView class is pretty much the same as using a regular View class, as usual, the incoming request is dispatched to an appropriate handler method such as .get() or .post().
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User
class ListUsers(APIView):
"""
View to list all users in the system.
* Requires token authentication.
* Only admin users are able to access this view.
"""
authentication_classes = [authentication.TokenAuthentication]
permission_classes = [permissions.IsAdminUser]
def get(self, request, format=None):
"""
Return a list of all users.
"""
usernames = [user.username for user in User.objects.all()]
return Response(usernames)
REST framework also allows you to work with regular function based views. It provides a set of simple decorators that wrap your function based views to ensure they receive an instance of Request (rather than the usual Django HttpRequest) and allows them to return a Response (instead of a Django HttpResponse), and allow you to configure how the request is processed.
@api_view() decoratorSignature: @api_view(http_method_names=['GET'])
The core of this functionality is the api_view decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data:
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view()
def hello_world(request):
return Response({"message": "Hello, world!"})
By default only GET methods will be accepted. Other methods will respond with “405 Method Not Allowed”. To alter this behaviour, specify which methods the view allows, like so:
@api_view(['GET', 'POST'])
def hello_world(request):
if request.method == 'POST':
return Response({"message": "Got some data!", "data": request.data})
return Response({"message": "Hello, world!"})
One of the key benefits of class-based views is the way they allow you to compose bits of reusable behavior. REST framework takes advantage of this by providing a number of pre-built views that provide for commonly used patterns.
The generic views provided by REST framework allow you to quickly build API views that map closely to your database models.
Note: If the generic views don’t suit the needs of your API, you can drop down to using the regular APIView class, or reuse the mixins and base classes used by the generic views to compose your own set of reusable generic views.
Typically when using the generic views, you’ll override the view, and set several class attributes.
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
For more complex cases you might also want to override various methods on the view class. For example.
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
def list(self, request):
# Note the use of `get_queryset()` instead of `self.queryset`
queryset = self.get_queryset()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
For very simple cases you might want to pass through any class attributes using the .as_view() method. For example, your URLconf might include something like the following entry:
path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
This class extends REST framework’s APIView class, adding commonly required behavior for standard list and detail views.
Note: Each of the concrete generic views provided is built by combining GenericAPIView, with one or more mixin classes.
Basic settings:
The following attributes control the basic view behavior.
queryset - The queryset that should be used for returning objects from this view. Typically, you must either set this attribute, or override the get_queryset() method. If you are overriding a view method, it is important that you call get_queryset() instead of accessing this property directly, as queryset will get evaluated once, and those results will be cached for all subsequent requests.serializer_class - The serializer class that should be used for validating and deserializing input, and for serializing output. Typically, you must either set this attribute, or override the get_serializer_class() method.lookup_field - The model field that should be used to for performing object lookup of individual model instances. Defaults to 'pk'. Note that when using hyperlinked APIs you’ll need to ensure that both the API views and the serializer classes set the lookup fields if you need to use a custom value.lookup_url_kwarg - The URL keyword argument that should be used for object lookup. The URL conf should include a keyword argument corresponding to this value. If unset this defaults to using the same value as lookup_field.Note: Check out the source code here
Base methods:
get_queryset(self)Returns the queryset that should be used for list views, and that should be used as the base for lookups in detail views. Defaults to returning the queryset specified by the queryset attribute.
This method should always be used rather than accessing self.queryset directly, as self.queryset gets evaluated only once, and those results are cached for all subsequent requests.
May be overridden to provide dynamic behavior, such as returning a queryset, that is specific to the user making the request.
For example:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
Note: If the serializer_class used in the generic view spans orm relations, leading to an n+1 problem, you could optimize your queryset in this method using
select_relatedandprefetch_related. To get more information about n+1 problem and use cases of the mentioned methods refer to related section in django documentation.
get_object(self)Returns an object instance that should be used for detail views. Defaults to using the lookup_field parameter to filter the base queryset.
May be overridden to provide more complex behavior, such as object lookups based on more than one URL kwarg.
For example:
def get_object(self):
queryset = self.get_queryset()
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj
Note that if your API doesn’t include any object level permissions, you may optionally exclude the self.check_object_permissions, and simply return the object from the get_object_or_404 lookup.
filter_queryset(self, queryset)Given a queryset, filter it with whichever filter backends are in use, returning a new queryset.
For example:
def filter_queryset(self, queryset):
filter_backends = [CategoryFilter]
if 'geo_route' in self.request.query_params:
filter_backends = [GeoRouteFilter, CategoryFilter]
elif 'geo_point' in self.request.query_params:
filter_backends = [GeoPointFilter, CategoryFilter]
for backend in list(filter_backends):
queryset = backend().filter_queryset(self.request, queryset, view=self)
return queryset
get_serializer_class(self)Returns the class that should be used for the serializer. Defaults to returning the serializer_class attribute.
May be overridden to provide dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.
For example:
def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
return BasicAccountSerializer
Save and deletion hooks:
The following methods are provided by the mixin classes, and provide easy overriding of the object save or deletion behavior.
perform_create(self, serializer) - Called by CreateModelMixin when saving a new object instance.perform_update(self, serializer) - Called by UpdateModelMixin when saving an existing object instance.perform_destroy(self, instance) - Called by DestroyModelMixin when deleting an object instance.These hooks are particularly useful for setting attributes that are implicit in the request, but are not part of the request data. For instance, you might set an attribute on the object based on the request user, or based on a URL keyword argument.
def perform_create(self, serializer):
serializer.save(user=self.request.user)
These override points are also particularly useful for adding behavior that occurs before or after saving an object, such as emailing a confirmation, or logging the update.
def perform_update(self, serializer):
instance = serializer.save()
send_email_confirmation(user=self.request.user, modified=instance)
You can also use these hooks to provide additional validation, by raising a ValidationError(). This can be useful if you need some validation logic to apply at the point of database save. For example:
def perform_create(self, serializer):
queryset = SignupRequest.objects.filter(user=self.request.user)
if queryset.exists():
raise ValidationError('You have already signed up')
serializer.save(user=self.request.user)
The mixin classes provide the actions that are used to provide the basic view behavior. Note that the mixin classes provide action methods rather than defining the handler methods, such as .get() and .post(), directly. This allows for more flexible composition of behavior.
The mixin classes can be imported from rest_framework.mixins.
Provides a .list(request, *args, **kwargs) method, that implements listing a queryset.
If the queryset is populated, this returns a 200 OK response, with a serialized representation of the queryset as the body of the response. The response data may optionally be paginated.
Provides a .create(request, *args, **kwargs) method, that implements creating and saving a new model instance.
If an object is created this returns a 201 Created response, with a serialized representation of the object as the body of the response. If the representation contains a key named url, then the Location header of the response will be populated with that value.
If the request data provided for creating the object was invalid, a 400 Bad Request response will be returned, with the error details as the body of the response.
Provides a .retrieve(request, *args, **kwargs) method, that implements returning an existing model instance in a response.
If an object can be retrieved this returns a 200 OK response, with a serialized representation of the object as the body of the response. Otherwise it will return a 404 Not Found.
Provides a .update(request, *args, **kwargs) method, that implements updating and saving an existing model instance.
Also provides a .partial_update(request, *args, **kwargs) method, which is similar to the update method, except that all fields for the update will be optional. This allows support for HTTP PATCH requests.
If an object is updated this returns a 200 OK response, with a serialized representation of the object as the body of the response.
If the request data provided for updating the object was invalid, a 400 Bad Request response will be returned, with the error details as the body of the response.
Provides a .destroy(request, *args, **kwargs) method, that implements deletion of an existing model instance.
If an object is deleted this returns a 204 No Content response, otherwise it will return a 404 Not Found.
The following classes are the concrete generic views. If you’re using generic views this is normally the level you’ll be working at unless you need heavily customized behavior.
The view classes can be imported from rest_framework.generics.
CreateAPIView
post method handler.ListAPIView
get method handler.RetrieveAPIView
get method handler.DestroyAPIView
delete method handler.UpdateAPIView
put and patch method handlers.ListCreateAPIView
get and post method handlers.RetrieveUpdateAPIView
get, put and patch method handlers.RetrieveDestroyAPIView
get and delete method handlers.RetrieveUpdateDestroyAPIView
get, put, patch and delete method handlers.Creating custom mixins
For example, if you need to lookup objects based on multiple fields in the URL conf, you could create a mixin class like the following:
class MultipleFieldLookupMixin:
"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
if self.kwargs[field]: # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
You can then simply apply this mixin to a view or viewset anytime you need to apply the custom behavior.
class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_fields = ['account', 'username']
Django REST framework allows you to combine the logic for a set of related views in a single class, called a ViewSet. In other frameworks you may also find conceptually similar implementations named something like ‘Resources’ or ‘Controllers’.
A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as .get() or .post(), and instead provides actions such as .list() and .create().
The method handlers for a ViewSet are only bound to the corresponding actions at the point of finalizing the view, using the .as_view() method.
Typically, 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.
Let’s define a simple viewset that can be used to list or retrieve all the users in the system.
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
If we need to, we can bind this viewset into two separate views, like so:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
Typically we wouldn’t do this, but would instead register the viewset with a router, and allow the urlconf to be automatically generated.
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
Rather than writing your own viewsets, you’ll often want to use the existing base classes that provide a default set of behavior. For example:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
There are two main advantages of using a ViewSet class over using a View class.
queryset once, and it’ll be used across multiple views.Both of these come with a trade-off. Using regular views and URL confs is more explicit and gives you more control. ViewSets are helpful if you want to get up and running quickly, or when you have a large API and you want to enforce a consistent URL configuration throughout.
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):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
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.name - the display name for the viewset. This argument is mutually exclusive to suffix.description - the display description for the individual view of a viewset.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.
"""
if self.action == 'list':
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAdminUser]
return [permission() for permission in permission_classes]
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 single object, or an entire collection. 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, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=True, methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.validated_data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
@action(detail=False)
def recent_users(self, request):
recent_users = User.objects.all().order_by('-last_login')
page = self.paginate_queryset(recent_users)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(recent_users, many=True)
return Response(serializer.data)
The action decorator will route GET requests by default, but may also accept other HTTP methods by setting the methods argument. For example:
@action(detail=True, methods=['post', 'delete'])
def unset_password(self, request, pk=None):
...
The decorator allows you to override any viewset-level configuration such as permission_classes, serializer_class, filter_backends…:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_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/$. Use the url_path and url_name parameters to change the URL segment and the reverse URL name of the action.
To view all extra actions, call the .get_extra_actions() method.
Extra actions can map additional HTTP methods to separate ViewSet methods. For example, the above password set/unset methods could be consolidated into a single route. Note that additional mappings do not accept arguments.
@action(detail=True, methods=['put'], name='Change Password')
def password(self, request, pk=None):
"""Update the user's password."""
...
@password.mapping.delete
def delete_password(self, request, pk=None):
"""Delete the user's password."""
...
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'])
'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'])
'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.
The ViewSet class inherits from APIView. You can use any of the standard attributes such as permission_classes, authentication_classes in order to control the API policy on the viewset.
The ViewSet class does not provide any implementations of actions. In order to use a ViewSet class you’ll override the class and define the action implementations explicitly.
The GenericViewSet class inherits from GenericAPIView, and provides the default set of get_object, get_queryset methods and other generic view base behavior, but does not include any actions by default.
In order to use a GenericViewSet class you’ll override the class and either mixin the required mixin classes, or define the action implementations explicitly.
The ModelViewSet class inherits from GenericAPIView and includes implementations for various actions, by mixing in the behavior of the various mixin classes.
The actions provided by the ModelViewSet class are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy().
Example
Because ModelViewSet extends GenericAPIView, you’ll normally need to provide at least the queryset and serializer_class attributes. For example:
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
Note that you can use any of the standard attributes or method overrides provided by GenericAPIView. For example, to use a ViewSet that dynamically determines the queryset it should operate on, you might do something like this:
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing the accounts
associated with the user.
"""
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
def get_queryset(self):
return self.request.user.accounts.all()
Note however that upon removal of the queryset property from your ViewSet, any associated router will be unable to derive the basename of your Model automatically, and so you will have to specify the basename kwarg as part of your router registration.
Also 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.
The ReadOnlyModelViewSet class also inherits from GenericAPIView. As with ModelViewSet it also includes implementations for various actions, but unlike ModelViewSet only provides the ‘read-only’ actions, .list() and .retrieve().
Example
As with ModelViewSet, you’ll normally need to provide at least the queryset and serializer_class attributes. For example:
class AccountViewSet(viewsets.ReadOnlyModelViewSet):
"""
A simple ViewSet for viewing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
Again, as with ModelViewSet, you can use any of the standard attributes and method overrides available to GenericAPIView.
You may need to provide custom ViewSet classes that do not have the full set of ModelViewSet actions, or that customize the behavior in some other way.
Example
To create a base viewset class that provides create, list and retrieve operations, inherit from GenericViewSet, and mixin the required actions:
from rest_framework import mixins
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
A viewset that provides `retrieve`, `create`, and `list` actions.
To use it, override the class and set the `.queryset` and
`.serializer_class` attributes.
"""
pass
By creating your own base ViewSet classes, you can provide common behavior that can be reused in multiple viewsets across your API.
ViewSets and Routers are simple tools to speed up the implementation of your API if you’re aiming for standard behaviour and standard URLs.
Using ViewSet you don’t have to create separate views for getting a list of objects and detail of one object. ViewSet will handle for you in a consistent way both list and detail.
Using Router will connect your ViewSet into “standarized” (it’s not standard in any global way, just some structure that was implemented by creators of Django REST framework) structure of URLs. That way you don’t have to create your urlpatterns by hand and you’re guaranteed that all of your URLs are consistent (at least on the layer that Router is responsible for).
It looks like not much, but when implementing some huge API where you will have many and many urlpatterns and views, using ViewSets and Routers will make big difference.
For better explanation: this is code using ViewSets and Routers:
views.py:
from snippets.models import Article
from rest_framework import viewsets
from yourapp.serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
urls.py:
from django.conf.urls import url, include
from yourapp import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'articles', views.ArticleViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
And equivalent result using normal Views and no routers:
views.py:
from snippets.models import Article
from snippets.serializers import ArticleSerializer
from rest_framework import generics
class ArticleList(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class ArticleDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
urls.py
from django.conf.urls import url, include
from yourapp import views
urlpatterns = [
url(r'articles/^', views.ArticleList.as_view(), name="article-list"),
url(r'articles/(?P<pk>[0-9]+)/^', views.ArticleDetail.as_view(), name="article-detail"),
]
Some 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.
REST 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.
Here’s an example of a simple URL conf, that uses SimpleRouter.
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = router.urls
There are two mandatory arguments to the register() method:
prefix - The URL prefix to use for this set of routes.viewset - The viewset class.Optionally, you may also specify an additional argument:
basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a querysetattribute then you must set basename when registering the viewset.The example above would generate the following URL patterns:
^users/$ Name: 'user-list'^users/{pk}/$ Name: 'user-detail'^accounts/$ Name: 'account-list'^accounts/{pk}/$ Name: 'account-detail'Note: The basename argument is used to specify the initial part of the view name pattern. In the example above, that’s the user or account part.
Typically you won’t need to specify the basename argument, but if you have a viewset where you’ve defined a custom get_queryset method, then the viewset may not have a .queryset attribute set. If you try to register that viewset you’ll see an error like this:
'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.
include with routersThe .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…
router = routers.SimpleRouter()
router.register(r'users', UserViewSet)
router.register(r'accounts', AccountViewSet)
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
]
urlpatterns += router.urls
Alternatively you can use Django’s include function, like so…
urlpatterns = [
path('forgot-password', ForgotPasswordFormView.as_view()),
path('', include(router.urls)),
]
You may use include with an application namespace:
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('api/', include((router.urls, 'app_name'))),
]
Or both an application and instance namespace:
urlpatterns = [
path('forgot-password/', ForgotPasswordFormView.as_view()),
path('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 asview_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.
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 action
class UserViewSet(ModelViewSet):
...
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
The following route would be generated:
^users/{pk}/set_password/$'user-set-password'By default, the URL pattern is based on the method name, and the URL name is the combination of the ViewSet.basenameand 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 action
class UserViewSet(ModelViewSet):
...
@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:
^users/{pk}/change-password/$'user-change_password'd model instances to be converted to native Python datatypes that can then be easily rendered into JSON, XML or other content types. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
The serializers in REST framework work very similarly to Django’s Form and ModelForm classes. We provide a Serializerclass which gives you a powerful, generic way to control the output of your responses, as well as a ModelSerializer class which provides a useful shortcut for creating serializers that deal with model instances and querysets.
Let’s start by creating a simple object we can use for example purposes:
from datetime import datetime
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
We’ll declare a serializer that we can use to serialize and deserialize data that corresponds to Comment objects.
Declaring a serializer looks very similar to declaring a form:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
To finalise the serialization process we render the data into json.
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
If we want to be able to return complete object instances based on the validated data we need to implement one or both of the .create() and .update() methods. For example:
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
def create(self, validated_data):
return Comment(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
return instance
If 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 Comment was a Django model, the methods might look like this:
def create(self, validated_data):
return Comment.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
Now when deserializing data, we can call .save() to return an object instance, based on the validated data.
comment = serializer.save()
Calling .save() will either create a new instance, or update an existing instance, depending on if an existing instance was passed when instantiating the serializer class:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
Note: Both the .create() and .update() methods are optional. You can implement either none, one, or both of them, depending on the use-case for your serializer class.
.save():Sometimes 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.
You can do so by including additional keyword arguments when calling .save(). For example:
serializer.save(owner=request.user)
Any additional keyword arguments will be included in the validated_data argument when .create() or .update() are called.
.save() directlyIn some cases the .create() and .update() 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.
In these cases you might instead choose to override .save() directly, as being more readable and meaningful.
For example:
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
Note that in the case above we’re now having to access the serializer .validated_data property directly.
When deserializing data, you always need to call is_valid() before attempting to access the validated data, or save an object instance. If any validation errors occur, the .errors property will contain a dictionary representing the resulting error messages. For example:
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
You can specify custom field-level validation by adding .validate_<field_name> methods to your Serializer subclass.
from rest_framework import serializers
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
content = serializers.CharField()
def validate_title(self, value):
"""
Check that the blog post is about Django.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("Blog post is not about Django")
return value
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.
To do any other validation that requires access to multiple fields, add a method called .validate() to your Serializersubclass. This method takes a single argument, which is a dictionary of field values.
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that start is before finish.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
When passing an initial object or queryset to a serializer instance, the object will be made available as .instance. If no initial object is passed then the .instance attribute will be None.
When passing data to a serializer instance, the unmodified data will be made available as .initial_data. If the datakeyword argument is not passed then the .initial_data attribute will not exist.
By default, serializers must be passed values for all required fields or they will raise validation errors. You can use the partialargument in order to allow partial updates.
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
The 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.
Note: The Serializer class is itself a type of Field, and can be used to represent relationships where one object type is nested inside another.
class UserSerializer(serializers.Serializer):
email = serializers.EmailField()
username = serializers.CharField(max_length=100)
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
If a nested representation may optionally accept the None value you should pass the required=False flag to the nested serializer.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Similarly if a nested representation should be a list of items, you should pass the many=True flag to the nested serializer.
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
.create() methods for nested representationsIf you’re supporting writable nested representations you’ll need to write .create() or .update() methods that handle saving multiple objects.
The following example demonstrates how you might handle creating a user with a nested profile object.
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user
.update() methods for nested representationsFor updates you’ll want to think carefully about how to handle updates to relationships. For example if the data for the relationship is None, or not provided, which of the following should occur?
NULL in the database.Here’s an example for an .update() method on our previous UserSerializer class.
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the following could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
Because 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 ModelSerializer``.create() and .update() methods do not include support for writable nested representations.
There are however, third-party packages available such as DRF Writable Nested that support automatic writable nested representations.
An alternative to saving multiple related instances in the serializer is to write custom model manager classes that handle creating the correct instances.
For example, suppose we wanted to ensure that User instances and Profile instances are always created together as a pair. We might write a custom manager class that looks something like this:
class UserManager(models.Manager):
...
def create(self, username, email, is_premium_member=False, has_support_contract=False):
user = User(username=username, email=email)
user.save()
profile = Profile(
user=user,
is_premium_member=is_premium_member,
has_support_contract=has_support_contract
)
profile.save()
return user
This manager class now more nicely encapsulates that user instances and profile instances are always created at the same time. Our .create() method on the serializer class can now be re-written to use the new manager method.
def create(self, validated_data):
return User.objects.create(
username=validated_data['username'],
email=validated_data['email'],
is_premium_member=validated_data['profile']['is_premium_member'],
has_support_contract=validated_data['profile']['has_support_contract']
)
For more details on this approach see the Django documentation on model managers, and this blogpost on using model and manager classes.
To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
# {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]
There 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.
You can provide arbitrary additional context by passing a context argument when instantiating the serializer. For example:
serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
The context dictionary can be used within any serializer field logic, such as a custom .to_representation() method, by accessing the self.context attribute.
The ModelSerializer class is the same as a regular Serializer class, except that:
.create() and .update().Declaring a ModelSerializer looks like this:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
By default, all the model fields on the class will be mapped to a corresponding serializer fields.
Any relationships such as foreign keys on the model will be mapped to PrimaryKeyRelatedField. Reverse relationships are not included by default unless explicitly included as specified in the serializer relations documentation.
ModelSerializerSerializer classes generate helpful verbose representation strings, that allow you to fully inspect the state of their fields. This is particularly useful when working with ModelSerializers where you want to determine what set of fields and validators are being automatically created for you.
To do so, open the Django shell, using python manage.py shell, then import the serializer class, instantiate it, and print the object representation…
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
If you only want a subset of the default fields to be used in a model serializer, you can do so using fields or exclude options, just as you would with a ModelForm. It is strongly recommended that you explicitly set all fields that should be serialized using the fields attribute. This will make it less likely to result in unintentionally exposing data when your models change.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
You can also set the fields attribute to the special value '__all__' to indicate that all fields in the model should be used.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
You can set the exclude attribute to a list of fields to be excluded from the serializer.
For example:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
In the example above, if the Account model had 3 fields account_name, users, and created, this will result in the fields account_name and created to be serialized.
The names in the fields and exclude attributes will normally map to model fields on the model class.
Alternatively names in the fields options can map to properties or methods which take no arguments that exist on the model class.
Since version 3.3.0, it is mandatory to provide one of the attributes fields or exclude.
The default ModelSerializer uses primary keys for relationships, but you can also easily generate nested representations using the depth option:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
If you want to customize the way the serialization is done you’ll need to define the field yourself.
You can add extra fields to a ModelSerializer or override the default fields by declaring fields on the class, just as you would for a Serializer class.
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
fields = ['url', 'groups']
Extra fields can correspond to any property or callable on the model.
You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the read_only=True attribute, you may use the shortcut Meta option, read_only_fields.
This option should be a list or tuple of field names, and is declared as follows:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
read_only_fields = ['account_name']
Model fields which have editable=False set, and AutoField fields will be set to read-only by default, and do not need to be added to the read_only_fields option.
Note: There is a special-case where a read-only field is part of a unique_together 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.
The right way to deal with this is to specify the field explicitly on the serializer, providing both the read_only=True and default=… keyword arguments.
One example of this is a read-only relation to the currently authenticated User which is unique_together with another identifier. In this case you would declare the user field like so:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
Please review the Validators Documentation for details on the UniqueTogetherValidator and CurrentUserDefault classes.
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the extra_kwargsoption. As in the case of read_only_fields, this means you do not need to explicitly declare the field on the serializer.
This option is a dictionary, mapping field names to a dictionary of keyword arguments. For example:
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
Note: Please keep in mind that, if the field has already been explicitly declared on the serializer class, then the extra_kwargs option will be ignored.
When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for ModelSerializer is to use the primary keys of the related instances.
Alternative representations include serializing using hyperlinks, serializing complete nested representations, or serializing with a custom representation.
For full details see the serializer relations documentation.
Relational fields are used to represent model relationships. They can be applied to ForeignKey, ManyToManyField and OneToOneField relationships, as well as to reverse relationships, and custom relationships such as GenericForeignKey.
Note: The relational fields are declared in relations.py, but by convention you should import them from the serializersmodule, using from rest_framework import serializers and refer to fields as serializers.<FieldName>.
Note: REST Framework does not attempt to automatically optimize querysets passed to serializers in terms of select_relatedand prefetch_related since it would be too much magic. A serializer with a field spanning an orm relation through its source attribute could require an additional database hit to fetch related object from the database. It is the programmer’s responsibility to optimize queries to avoid additional database hits which could occur while using such a serializer.
For example, the following serializer would lead to a database hit each time evaluating the tracks field if it is not prefetched:
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='title'
)
class Meta:
model = Album
fields = ['album_name', 'artist', 'tracks']
# For each album object, tracks should be fetched from database
qs = Album.objects.all()
print(AlbumSerializer(qs, many=True).data)
If AlbumSerializer is used to serialize a fairly large queryset with many=True then it could be a serious performance problem. Optimizing the queryset passed to AlbumSerializer with:
qs = Album.objects.prefetch_related('tracks')
# No additional database hits required
print(AlbumSerializer(qs, many=True).data)
would solve the issue.