Django-View Components

The main role of the views provided by Django REST framwork:
1. Control the execution of the serializer (check, save, transform data)
2. Control the execution of database queries

One, two view base classes

Conclusion first:

APIView: if followed by models No relationship (no database related operations), just inherit it
GenericAPIView: For database operations, queryset and serializer_class

1.APIView

Inheriting the View of native Django, we need to write the code corresponding to the request method ourselves, such as

class BookAPIView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        ser = serializer.BookModelSerializer(book_list, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = serializer.BookModelSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
        	return Response(ser.data)
        return Response(ser.error)
    
class BookDetailAPIView(APIView):
    def get(self, request, pk):
        book = models.Book.objects.get(id=pk)
        ser = serializer.BookModelSerializer(book)
        return Response(ser.data)

    def put(self, request, pk):
        book = models.Book.objects.get(id=pk)
        ser = serializer.BookModelSerializer(book, data=request.data)
        if ser.is_valid():
            ser.save()
        return Response('Successfully modified')

    def delete(self, request, pk):
        models.Book.objects.filter(id=pk).delete()
        return Response('successfully deleted')

2.GenericAPIView

Inheriting APIView, considering that when using APIView, when we write multiple classes, the only changed code is the called myserializers class and its own table model, so the variables are extracted.

queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
get_queryset: get configured queryset
get_object: Used when querying a single record, the grouping field in the route must be pk
get_serializer: Get the serialization class of the configuration    
class BookGenericView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

    def get(self, request, *args, **kwargs):
        obj = self.get_queryset()
        ser = self.get_serializer(obj, many=True)
        return Response(ser.data)
    def post(self, request, *args, **kwargs):
        ser=self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
        return Response('success')
    
class BookDetailGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

    def get(self, request, *args, **kwargs):
        obj = self.get_object()
        ser = self.get_serializer(obj)
        return Response(ser.data)

    def put(self, request, *args, **kwargs):
        obj = self.get_object()
        ser = self.get_serializer(obj, data=request.data)
        if ser.is_valid():
            ser.save()
        return Response('Successfully modified')

    def delete(self, request, *args, **kwargs):
        # self.get_object().delete() or the method below
        self.queryset.filter(id=kwargs.get('pk')).delete()
        return Response('successfully deleted')

Two, five view extension classes

When we use the GenericAPIView class to extract the variables, we will find that we actually have the same internal code for different interfaces, so we encapsulate it, that is, extract each request method and encapsulate it into a separate class.

from rest_framework.mixins import ...
CreateModelMixin: create method to create a
DestroyModelMixin: destory method to delete
ListModelMixin: list method to get all
RetrieveModelMixin: retrieve get one
UpdateModelMixin: update modify one
class BookGenericView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)


class BookDetailGenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

Three, nine subclass views

The subclass view is a further encapsulation of the extension class, because we still have repeated code in the previous part. Since these request methods come in, the execution logic is the same, so I will further encapsulate it.

Five extension classes, divided into group get, post, single get, put, delete. In our common classes, there are generally one class for group get, one class for post, and one class for the other three methods, so they are combined together, there are exactly nine sub-class views, namely 2+1, 3+2+1, and there are nine combinations in total.

#9 subclassed views (rest_framework.generics)
CreateAPIView:inherit CreateModelMixin,GenericAPIView,Have post method, add data
DestroyAPIView: inherit DestroyModelMixin,GenericAPIView,Have delete method, delete data
ListAPIView: inherit ListModelMixin,GenericAPIView,Have get method to get all
UpdateAPIView: inherit UpdateModelMixin,GenericAPIView,Have put and patch method, modify data
RetrieveAPIView: inherit RetrieveModelMixin,GenericAPIView,Have get method to get a


ListCreateAPIView: inherit ListModelMixin,CreateModelMixin,GenericAPIView,Have get get all, post method added
RetrieveDestroyAPIView: inherit RetrieveModelMixin,DestroyModelMixin,GenericAPIView,Have get method to get one, delete method delete
RetrieveUpdateAPIView: inherit RetrieveModelMixin,UpdateModelMixin,GenericAPIView,Have get get one, put,patch Revise
RetrieveUpdateDestroyAPIView: inherit RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,Have get get one, put,patch Revise, delete delete

The following is the most complete way. The nine subclass views are more flexible and can be combined arbitrarily.

from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView

class BookGenericView(ListCreateAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

class BookDetailGenericAPIView(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

Fourth, the view set

When we have encapsulated the subclass view, we will find that the repetition of the code has appeared again. The two classes we wrote above actually have the same code, so they have been encapsulated again. In general, we use ModelViewSet or ReadOnlyModelViewSet, but the view set contains more than these two.

from rest_framework.viewsets import ModelViewSet # Contains five request methods
from rest_framework.viewsets import ReadOnlyModelViewSet # Only query, single record query and group query

class BookSetView(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer

1. ViewSet

Inherited from APIView and ViewSetMixin, the function is basically similar to APIView, providing identity authentication, permission verification, traffic management, etc.

ViewSet mainly implements the mapping processing of incoming dictionaries (such as {'get':'list'}) when calling as_view() by inheriting ViewSetMixin.

In ViewSet, no action method is provided, and we need to implement the action method ourselves.

2.GenericViewSet

It is usually inconvenient to use ViewSet, because methods such as list, retrieve, create, update, and destroy all need to be written by yourself, and these methods have the same name as the methods provided by the Mixin extension class mentioned above, so we can duplicate the method by inheriting the Mixin extension class. Use these methods without writing your own. But the Mixin extension class depends on GenericAPIView, so it also needs to inherit GenericAPIView.

GenericViewSet helps us complete such inheritance work. It inherits from GenericAPIView and ViewSetMixin. While implementing the mapping processing of incoming dictionaries (such as {'get':'list'}) when calling as_view(), it also provides The basic methods provided by GenericAPIView can be used directly with the Mixin extension class.

Example:

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer

definition of url

urlpatterns = [
    path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
    re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})),

]

3.ModelViewSet

Inherited from GenericViewSet, including ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestoryModelMixin.

4.ReadOnlyModelViewSet

Inherited from GenericViewSet, including ListModelMixin and RetrieveModelMixin.

5. Source code analysis

The ViewSet view set class no longer implements get(), post() and other methods, but implements action s such as list(), create() and so on. Because we have now encapsulated all actions, there will be problems when single get and group get requests, because both are get requests, and the requests are sent to the same class. Here, the framework author uses it very cleverly. method to solve this problem. The solution is to match the corresponding method in the routing. code show as below

path('books_set/', views.BookSetView.as_view({'get':'list','post':'create'})),
re_path('^books_set/(?P<pk>\d+)', views.BookSetView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

The as_view method in the native class definitely does not support this approach, so the ModelViewSet must override the as_view() method

Five, the use of routing

Generally, if the view class of drf is used, it will be used together with the routing component, because the routing component can help us automatically generate the routing path

1. Simple use of routing components

# Automatically generate routes, the following two are used more
SimpleRouter and DefaultRouter

# The view class that inherits ViewSetMixin, and writes routing later, can be automatically generated and written in urls.py
from rest_framework import routers
# instantiate an object
router = routers.SimpleRouter()
# Register into the route
router.register('books', views.BookSetView)
# Configure the automatically generated route to urlpatterns
	-urlpatterns += router.urls  # or the following way
    -re_path(r'v1/', include(router.urls))  
    # The v1 here refers to adding the v1 path before the generated path, such as the original login, now it becomes v1/login

2. How to configure routing

# three ways
1.most primitive
	-path('books/', views.BookAPIView.as_view()),
2.ViewSetMixin view class
	-path('books_set/', views.BookSetView.as_view({'get':'list','post':'create'}))
3.ViewSetMixin view class
-Automatically generated, as mentioned above

3.action

-When the route is automatically generated, because there are other methods in the view class, the route cannot be automatically generated.
-add action Decorator:
	-methods: What request method will trigger the execution of the decorated function
    -detail: Yes True is based on the belt id generated by the route, if it is False,is based on not having id generated by the route
	-@action(methods=['get'], detail=True)

Tags: Django

Posted by rpanning on Thu, 05 May 2022 04:41:18 +0300