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.