Django REST Framework API Guide (1) : Requesting Django REST Framework API Guide (2) : Responding to Django REST Framework API Guide (3) : Views Django REST Framework API Guide (4) : Generic Views Django REST Framework API Guide (5) : View Sets Django REST Framework API Guide (6) : Routing Django REST Framework API Guide (7) : Parsing
Link to official text
View the set
After routing determines which controller is used for the request, the controller is responsible for understanding the request and producing the appropriate output. – Ruby on Rails documentation
The Django REST Framework allows you to group the logic of a set of related views into a class called a ViewSet. In other frameworks, you might find conceptually similar implementations called “Resources” or “Controllers.”
The ViewSet class is simply a class-based View that does not provide any processing methods, such as.get() or.post(), but instead provides operations such as.list() and.create().
The ViewSet only does something when bound to the final view with the.as_view() method.
Typically, instead of explicitly registering views in the view set in urlConf, the view set is registered using the router class, which automatically determines the URLConf for you.
Take a chestnut
Define a simple set of views that can be used to list or retrieve all users in a 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):
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)
Copy the code
If necessary, you can assemble this view into two separate views, as shown below:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
Copy the code
Normally, we don’t do this, but instead register the set of views with the router and allow the URLConf to be generated automatically.
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='user')
urlpatterns = router.urls
Copy the code
Instead of writing your own view set, you usually use existing base classes provided by default. Such as:
class UserViewSet(viewsets.ModelViewSet):
""" View for viewing and editing user instances. "" "
serializer_class = UserSerializer
queryset = User.objects.all()
Copy the code
Using a ViewSet has two main advantages over using the View class.
- Repeated logic can be combined into a single class. In the example above, we only need to specify the query set once, which will be used in multiple views.
- With the use of routers, we no longer need to deal with our own URL configuration.
Both have their advantages and disadvantages. Using regular views and URL profiles is more explicit and gives you more control. View assemblies are useful if you want to develop an application more quickly, or if you need to keep the URL configuration of a large API consistent.
Action View set
The default routes included in the REST framework will route a standard set of CREATE/Retrieve/Update/destroy style actions as follows:
class UserViewSet(viewsets.ViewSet):
""" These methods will be handled by the router. If you want to use a suffix, be sure to include the 'format = None' keyword argument """
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
Copy the code
During scheduling, the name of the current action is available via the.action property. You can check.action to adjust behavior based on the current action.
For example, you can restrict access to actions other than the list to only admin, as follows:
def get_permissions(self):
Instantiate and return a list of permissions required by this view. "" "
if self.action == 'list':
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAdmin]
return [permission() for permission in permission_classes]
Copy the code
Flags additional routing behavior
If you need to route specific methods, you can decorate them with the @detail_route or @list_route decorator.
The @detail_route decorator includes PK in its URL schema to support methods that need to fetch a single instance. The @list_route decorator applies to methods that operate on a list of objects.
Here’s an example:
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.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
class UserViewSet(viewsets.ModelViewSet):
"" set of views that provide standard operations. ""
queryset = User.objects.all()
serializer_class = UserSerializer
@detail_route(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.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
@list_route()
def recent_users(self, request):
recent_users = User.objects.all().order('-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)
Copy the code
In addition, decorators can set additional parameters for the routing view. For example…
@detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):.Copy the code
These decorators route GET requests by default, but can also accept other HTTP methods using the methods parameter. Such as:
@detail_route(methods=['post', 'delete'])
def unset_password(self, request, pk=None):.Copy the code
The two new actions will be on urls ^users/{pk}/set_password/$and ^users/{PK}/unset_password/$.
The action to jump
If you need to get the URL of the action, use the.reverse_action() method. This is a handy wrapper around.reverse(), which automatically passes the view’s request object and hooks the URl_name to the.basename property.
Note that basename is provided by the router during the ViewSet registration process. If you are not using a router, you must supply 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'
Copy the code
The url_name argument should match the same arguments for the @list_route and @detail_route decorators. In addition, this can be used to reverse the default list and detail routes.
API reference
ViewSet
The ViewSet class inherits from APIView. You can use any of the standard attributes (such as permission_classes, authentication_classes) to control the API policy on the view.
The ViewSet class does not provide any implementation of an action. In order to use the ViewSet class, you must inherit it and explicitly define the action implementation.
GenericViewSet
The GenericViewSet class inherits from GenericAPIView and provides default get_object, get_querySet methods, and other common view base behavior, but contains no operations by default.
In order to use the GenericViewSet class, you must either inherit the class and mix the required mixin classes, or explicitly define the operation implementation.
ModelViewSet
The ModelViewSet class inherits from GenericAPIView and contains implementations of various operations by mixing the behavior of various mixin classes.
The operations provided by ModelViewSet are.list(),.Retrieve (),.create(),.update(),.partial_update(), and.destroy().
Here’s an example:
Because the ModelViewSet class inherits from GenericAPIView, you usually need to provide at least querySet and SerializER_class attributes. Such as:
class AccountViewSet(viewsets.ModelViewSet):
Used to view and edit accounts
queryset = Account.objects.all()
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
Copy the code
Note that you can override any of the standard properties or methods provided by GenericAPIView. For example, to dynamically determine the ViewSet of the query set that it should operate on, do 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()
Copy the code
Note, however, that after removing the QuerySet attribute from the ViewSet, any associated router will not automatically export the base_name of the model, so you must specify Base_name kwarg as part of the Router registry.
Also note that while this class provides a complete set of create/list/Retrieve/update/destroy operations by default, you can restrict the available operations by using standard permission classes.
ReadOnlyModelViewSet
The ReadOnlyModelViewSet class also inherits from GenericAPIView. Like ModelViewSet, it contains implementations of various operations, but unlike ModelViewSet it only provides “read-only” operations,.list() and.Retrieve ().
Here’s an example:
As with ModelViewSet, you usually need to provide at least the QuerySet and SerializER_class attributes. Such as:
class AccountViewSet(viewsets.ReadOnlyModelViewSet):
""" A simple ViewSet for viewing accounts. """
queryset = Account.objects.all()
serializer_class = AccountSerializer
Copy the code
Also, as with ModelViewSet, you can override any standard properties and methods available to GenericAPIView.
Custom view set base class
You may need to use custom ViewSet classes without a full set of ModelViewSet actions, or other custom behavior.
Here’s an example:
To create the basic view set class that provides the CREATE, List, and Retrieve operations, inherit from GenericViewSet and mix the required operations:
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
Copy the code
By creating your own basic ViewSet class, you can provide common behavior that can be reused across multiple view sets in the API.