Django REST Framework——5. View and routing

Posted by mmonaco on Sun, 19 Dec 2021 02:59:35 +0100


A key benefit of class based views is that they allow you to combine some reusable behaviors. DRF takes advantage of this by providing a large number of pre built views, which provides us with some common patterns. What we need to do is to select the appropriate view class for inheritance.

1, APIView class

The APIView class provided by DRF is a subclass of Django's View class. It is different from the general View class in the following ways:

  • The Request passed into the processing method is not an instance of the HttpRequest class of Django, but an instance of the Request class of DRF.
  • The processing method can return the Response of the DRF, not just the HttpRequest of Django. The view manages the content protocol and sets the correct renderer for the Response.
  • Any APIException is caught and passed to the appropriate response.
  • Incoming requests are authenticated and appropriate permission or frequency limit checks are run before the request is dispatched to the processing method.

Using the APIView class is very similar to using the general View class. Usually, the incoming request will be distributed to the appropriate processing methods, such as get(), post(). Our previous views inherited APIView.

2, GenericAPIView class

This class is a subclass of APIView and extends the APIView class, Some methods used by specific general views (as will be learned later, each view has a specific application scenario) are added. Each specific general view is constructed by combining GenericAPIView with one or more mixin classes, that is, GenericAPIView and these mixin classes are the parent classes of specific general views.

2.1 common attributes

  • queryset:

    Sets the query set of objects returned from this view. For example, queryset = book Objects, and then the model objects will be obtained from the query set.

    You must set this property or override get_queryset() method.

    If you want to override a view method, you should call get_ It is important to use the queryset () method instead of accessing the property directly. Because after the DRF obtains the value of queryset for the first time, it will be cached for all subsequent requests.

  • serializer_class:

    Sets the Serializer class used to validate, deserialize input, and serialize output. You must set this property or override get_serializer_class() method.

  • lookup_field: the field referenced when finding a single model object. The default is' pk '.

    That is, after the parameter named pk is captured from the route and passed into the view, when the model object is obtained according to pk, DRF will automatically use pk as the search condition to find the target object. We don't need to write filter(pk=pk).

2.2 common methods

  • get_object(self):

    Get the basic query set from the model class set by the queryset property, and then use lookup_ The field property filters the basic query set and finally returns an object instance.

  • get_queryset(self):

    Returns the query set of the model class specified by the queryset property.

  • get_serializer_class(self):

    Serializer is returned by default_ The value of the class property, that is, the specified serializer class.

  • get_serializer(self, instance=None, data=None, many=False, partial=False):

    Return serializer_ An instance of the serializer class specified by the class property.

Let's understand the above methods through a book related example (model class and serializer class are not the focus, write it yourself 😘):

class BookDetailView(GenericAPIView):
    # Set the query set to use
    queryset = Book.objects.all()
    # Sets the serializer to use
    serializer_class = BookSerializer
    # Set the field used to find model objects. The default is "pk"
    # Because the parameters captured in the route are named "id", they should also be changed to "id"
    lookup_field = "id"

    def get(self, request, id):
        """Returns information about a single book"""
        # When a single object is obtained, it is automatically searched by id
        book = self.get_object()  # Equivalent to book = book objects. filter(id=id). first()
        # Gets the serializer class and instantiates it
        book_ser = self.get_serializer(book)
        return Response(book_ser.data)

    def put(self, request, id):
        """Modify the information of a single book"""
        book = self.get_object()
        # Get the serializer class, instantiate it, and pass in the data submitted by the front end
        book_ser = self.get_serializer(instance=book, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': 'Verification failed'})

    def delete(self, request, id):
        """Delete a single book"""
        # Automatically delete objects by id
        self.get_object().delete()
        return Response({'status': 100, 'msg': 'Delete succeeded'})


class BooksView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        """Get all books"""
        queryset = Book.objects
        serializer_class = BookSerializer
        # Gets the query set of the model class
        book_list = self.get_queryset()
        book_ser = self.get_serializer(book_list, many=True)
        return Response(book_ser.data)

    def post(self, request):
        """Add a Book"""
        book_ser = self.get_serializer(data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response({'status': 101, 'msg': 'Verification failed'})

3, Mixin class

The mixin class provides some methods for specific general views, such as create(), update(), and so on. These mixin classes are specifically used to compose and build specific common views.

The mixin class can be from rest_frame.mixins import.

  • ListModelMixin:

    Provide a list(request, *args, **kwargs) method to return the query set.

  • CreateModelMixin:

    Provide the create(request, *args, **kwargs) method to create and save a new model instance.

  • UpdateModelMixin:

    Provide a retrieve(request, *args, **kwargs) method to return an existing model instance in the response.

  • DestroyModelMixin:

    An update(request, * args, * * kwargs) method is provided to update and save existing model instances.

    A partial is also provided_ The update (request, * args, * * kwargs) method is similar to the update method, except that all fields used for update are optional. This allows support for HTTP PATCH requests.

  • RetrieveModelMixin:

    A destroy(request, * args, * * kwargs) method is provided to delete existing model instances.

4, Generic concrete view class

The following classes are specific general views. These are what we usually use. Unless in-depth customization is required, we can directly inherit and use them.

These view classes are available from rest_framework.generics import.

  • CreateAPIView:

    It is used to create a specific view of the model instance and provide a post method handler.

    For example, if we want to create a book, we only need to inherit CreateAPIView, which implements the post method and the operation of creating model instances and saving them to the database:

    class BookDetailView(CreateAPIView):
        # Set the model class to be used
        queryset = Book.objects.all()
        # Sets the serializer to use
        serializer_class = BookSerializer
        # Set the field used to find model objects. The default is "pk", which is changed to "id"“
        lookup_field = "id"
    

    After setting the route and submitting the book information using the post method, you can directly create the book record.

The use methods of the following types are similar, which needs to inherit which.

  • ListAPIView:

    It is used to list the specific view of the query set (get all), and provides a get method handler.

  • RetrieveAPIView:

    It is used to retrieve the specific view of the model instance (get the specified single), and provides a get method handler.

  • DestroyAPIView:

    It is used to delete the specific view of the model instance, and provides a delete method handler.

  • UpdateAPIView:

    It is used to update the specific view of the model instance and provide a put and patch method handler.

  • ListCreateAPIView:

    List the specific views of query sets or create model instances, and provide a handler for get and post methods.

    In other words, it is a combination of ListAPIView and CreateAPIView.

The same is true for the following classes, just to facilitate writing and combine multiple classes together.

  • RetrieveUpdateAPIView:

    It is used to retrieve and update the specific view of the model instance, and provides the handler of get, put and patch methods.

  • RetrieveDestroyAPIView:

    It is used to retrieve or delete specific views of model instances, and provides handlers for get and delete methods.

  • RetrieveUpdateDestroyAPIView:

    It is used to retrieve, update or delete specific views of model instances, and provides handlers for get, put, patch and delete methods.

Finally, let's take a look at what the previous book examples look like after using the specific general view class?

from rest_framework.generics import CreateAPIView,ListAPIView, RetrieveUpdateDestroyAPIView

from quickstart.models import Book
from quickstart.serializers import BookSerializer

# Create your views here.


class BookDetailView(CreateAPIView, RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    lookup_field = "id"


class BooksView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

Yes, it's so simple. But it can be more concise.

In the above code, queryset and serializer_class and other attributes are still repeated. In the final analysis, there is only one get() method, but we need to obtain a single book or a book list through the http get method. There is a conflict between the two, so we can only divide them into two views for processing respectively. DRF also takes this into account, so it provides the function of view set.

5, View sets and routers

5.1 view set

DRF allows us to combine the logic of a set of related views into a class called ViewSet.

ViewSet is just a class based View type. It does not provide any method handlers, such as get() or post(), but provides handlers such as list() and create(). Therefore, the http request method needs to be bound to the corresponding handler in the route.

For example, through as_view() method, get and post are http request methods, list and create are corresponding handlers:

urlpatterns = [
    path('snippets/', BookViewSet.as_view(actions={
    'get': 'list',
    'post': 'create'})
    )]

But usually, we register the view and bind the method through the Router (an instance of the Router class), instead of explicitly binding the view using methods such as path() as before.

5.2 Router

As long as we use the ViewSet class (precisely because it should be the ViewSetMixin class, because the as_view() method is overridden by it) instead of the View class, we don't need to design the URL ourselves. All we need to do is register the corresponding View set with the router and let it do the rest.

5.2. 1. Application method

from django.urls import path, include
from rest_framework.routers import DefaultRouter

from quickstart import views

# Create a router and register our view set
router = DefaultRouter()
# The first parameter is the prefix of the url, and the latter is the corresponding view
router.register(r'books', views.BooksViewSet)  # Prefix without slash

# The API url is now automatically determined by the router
urlpatterns = [
    path('', include(router.urls))
]

# You can also add it directly without using include
# urlpatterns += router.urls

The above example will automatically generate the following URL pattern and save it in router In URLs:

  • URL pattern: ^books/$ Name: 'book-list'
  • URL pattern: ^books/{pk}/$ Name: 'book-detail'

The DefaultRouter class also automatically creates an API ROOT view for us, that is, the root path and its corresponding view. Therefore, if there was an API ROOT route or view, it can be deleted now.

5.2.2 DefaultRouter class

URL StyleHTTP methodactionURL name
[.format]GETautomatically generated root viewapi-root
{prefix}/[.format]GETlist{basename}-list
POSTcreate
{prefix}/{methodname}/[.format]GET, or as specified by `methods` argument`@list_route` decorated method{basename}-{methodname}
{prefix}/{lookup}/[.format]GETretrieve{basename}-detail
PUTupdate
PATCHpartial_update
DELETEdestroy
{prefix}/{lookup}/{methodname}/[.format]GET, or as specified by `methods` argument`@detail_route` decorated method{basename}-{methodname}
  • Generate suffix:

    DefaultRouter can also generate optional json style format suffix routing.

    For example, access the path books. Com from the browser json, he will return the data in json format instead of the default HTML page.

  • Delete the trailing slash of the url:

    # When instantiating, pass in the following parameters
    router = DefaultRouter(trailing_slash=False)
    

5.3 ModelViewSet class

After learning so many view classes, there are complex inheritance relationships between them, and now the brain is in chaos 😵 ‍ 💫, I don't know which classes to inherit. But fortunately, DRF has considered this problem in advance and provided us with ModelViewSet class.

The ModelViewSet class inherits from GenericAPIView and various mixin classes. Therefore, it contains the implementation of various operations.

Therefore, if we decide to use the view set, we can inherit the ModelViewSet class.

The operations provided by ModelViewSet class include list(), retrieve(), create(), update (), partial_update() and destroy().

Finally, take a look at the final form of the book example:

View:

from rest_framework.viewsets import ModelViewSet

from quickstart.models import Book
from quickstart.serializers import BookSerializer


class BookDetailView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    lookup_field = "id"

route:

from django.urls import include, path
from rest_framework.routers import DefaultRouter

from quickstart import views


router = DefaultRouter()
router.register(r'books', views.BookDetailView)


urlpatterns = [
    path('', include(router.urls))
]

Topics: Python Django Back-end RESTful