DRF Views Inheritance Relationships

OverView

 

Django View -- Root

https://docs.djangoproject.com/en/3.2/ref/class-based-views/base/#view

所有view视图的基类, 此类不是通用视图,被业务视图继承。

class django.views.generic.base.View

The master class-based base view. All other class-based views inherit from this base class. It isn’t strictly a generic view and thus can also be imported from django.views.

主要功能是做映射:  http method --> view method

HTTP METHOD -->  VIEW.METHOD

GET     -->  View.get

POST     -->  View.post

PUT     -->  View.put

DELETE   -->  View.delete

视图业务定义在 http method名称的方法中。

from django.http import HttpResponse
from django.views import View

class MyView(View):

    def get(self, request, *args, **kwargs):
        return HttpResponse('Hello, World!')

使用as_view方法,将视图类转换为 callable 视图。

from django.urls import path

from myapp.views import MyView

urlpatterns = [
    path('mine/', MyView.as_view(), name='my-view'),
]

DRF APIView --> Django View

 https://www.django-rest-framework.org/api-guide/views/

继承了Dangjo View,

  同Django View相同点:

    有 HTTP METHOD 到 VIEW METHOD的映射

    业务方法定义在 VIEW METHOD 方法中

  不同的地方:

    新增了一些控制属性

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(). Additionally, a number of attributes may be set on the class that control various aspects of the API policy.

DRF 控制属性

https://testdriven.io/blog/drf-views-part-1/

AttributeUsageExamples
renderer_classes determines which media types the response returns JSONRenderer, BrowsableAPIRenderer
parser_classes determines which data parsers for different media types are allowed JSONParser, FileUploadParser
authentication_classes determines which authentication schemas are allowed for identifying the user TokenAuthentication, SessionAuthentication
throttle_classes determines if a request should be authorized based on the rate of requests AnonRateThrottle, UserRateThrottle
permission_classes determines if a request should be authorized based on user credentials IsAuthenticated, DjangoModelPermissions
content_negotiation_class selects one of the multiple possible representations of the resource to return to a client (unlikely you'll want to set it up) only custom content negotiation classe

在view.method 之外增加 控制属性

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)

DRF GenericAPIView --> DRF APIView

https://www.django-rest-framework.org/api-guide/generic-views/#genericapiview

DRF APIView 相比 Django View, 增加了一些控制属性, 例如对接口的访问权限和限流进行控制。

DRF GenericAPIView 相比 DRF APIView,又增加了一些控制属性, 这些控制属性,是专门面向典型的多实例数据(数据库表)设计,

让用户简单定义一个API, 可以完成对应一个数据库表的CRUD操作。

This class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views.

如下三个:

queryset --> 使用Model定义查询集合

serializer_class --> 定义模型的序列化类

pagination_class --> 分页控制

  • 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.
  • pagination_class - The pagination class that should be used when paginating list results. Defaults to the same value as the DEFAULT_PAGINATION_CLASS setting, which is 'rest_framework.pagination.PageNumberPagination'. Setting pagination_class=None will disable pagination on this view.

https://testdriven.io/blog/drf-views-part-2/#genericapiview

基于新增的控制属性, 可以非常方便地实现 GET 和 DELETE 业务。

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response

class RetrieveDeleteItem(GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

    def delete(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 

DRF Concrete View Classes --> DRF GenericAPIView

DRF GenericAPIView 是从数据层面上定义了一些公共属性, 这些属性服务于单个数据库表,

使得在 get delete方法中,可以方便地操作数据, 和 序列化数据, 见上节GenericAPIView示例代码。

如果后台逻辑非常简单,仅仅局限在数据库表的 CRUD操作, 那么get delete方法中的代码就存在公用性,

更好的方法是将这些公共的代码,封装起来(Mixins),使用的时候继承使用。

Mixins

在各个Mixin中封装好 view.method中的公用代码。

https://www.django-rest-framework.org/api-guide/generic-views/#mixins

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.

所有的Mixin都是不能单独使用的, 需要个GenericAPIView搭配使用。

但是这些Mixin仅仅做公共代码逻辑的封装,并不提供view.method的实现。

https://testdriven.io/blog/drf-views-part-2/#mixins

Mixins provide bits of common behavior. They cannot be used standalone; they must be paired with GenericAPIView to make a functional view. While the mixin classes provide create/retrieve/update/delete actions, you still need to bind the appropriate actions to the methods.

MixinUsage
CreateModelMixin Create a model instance
ListModelMixin List a queryset
RetrieveModelMixin Retrieve a model instance
UpdateModelMixin Update a model instance
DestroyModelMixin Delete a model instance

例如 RetrieveModelMixin 仅仅实现 retrieve 方法。

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

基于此, 可以使得 view.method更加简洁, 例如。

实际上,如无特殊逻辑(数据库表操作之外的逻辑), 考虑到通用性, get 和 post的方法, 也不应该让用户维护。

from rest_framework import mixins
from rest_framework.generics import GenericAPIView

class CreateListItems(mixins.ListModelMixin, mixins.CreateModelMixin, GenericAPIView):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

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

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

Concrete View Classes

具体的视图类,就是为了考虑的数据库表操作的通用性, 竭尽所能,让用户少维护代码。

https://www.django-rest-framework.org/api-guide/generic-views/#concrete-view-classes

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.

例如ListCreateAPIView, 实现了 Mixin.method 绑定到 View.method中。

https://testdriven.io/blog/drf-views-part-2/#concrete-views

# https://github.com/encode/django-rest-framework/blob/3.12.4/rest_framework/generics.py#L232

class ListCreateAPIView(mixins.ListModelMixin,
                        mixins.CreateModelMixin,
                        GenericAPIView):

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

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

具体使用上,就变成了声明式, 无需关心 get 和 post方法实现:

from rest_framework.generics import ListCreateAPIView

class ListCreateItems(ListCreateAPIView):

    authentication_classes = [TokenAuthentication]

    queryset = Item.objects.all()
    serializer_class = ItemSerializer

具体视图类包括以下若干种:

https://testdriven.io/blog/drf-views-part-2/#concrete-views

ClassUsageMethod handlerExtends mixin
CreateAPIView create-only post CreateModelMixin
ListAPIView read-only for multiple instances get ListModelMixin
RetrieveAPIView read-only for single instance get RetrieveModelMixin
DestroyAPIView delete-only for single instance delete DestroyModelMixin
UpdateAPIView update-only for single instance put, patch UpdateModelMixin
ListCreateAPIView read-write for multiple instances get, post CreateModelMixin, ListModelMixin
RetrieveUpdateAPIView read-update for single instance get, put, patch RetrieveModelMixin, UpdateModelMixin
RetrieveDestroyAPIView read-delete for single instance get, delete RetrieveModelMixin, DestroyModelMixin
RetrieveUpdateDestroyAPIView read-update-delete for single instance get, put, patch, delete RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

ViewSet --> APIView, ViewSetMixin

ViewSet 继承于 APIView,

  拥有两个能力:

    (1)HTTP.METHOD 到 VIEW.METHOD 映射能力 (django view)

    (2)定义接口访问控制属性 (DRF APIView)

  其还继承了 ViewSetMixin

    此类重写了 as_view 接口, 将HTTP.METHOD映射到MIXIN.ACTION. 这样 业务开发者只需要关注ACTION 接口实现。

ViewSetMixin

https://testdriven.io/blog/drf-views-part-3/#viewset-class

ViewSetMixin is a class where all the "magic happens". It's the only class that all four of the ViewSets share. It overrides the as_view method and combines the method with the proper action.

MethodList / DetailAction
post List create
get List list
get Detail retrieve
put Detail update
patch Detail partial_update
delete Detail destroy

ViewSet

继承ViewSet的子类(业务类)需要实现 VIEW.ACTION.

https://www.django-rest-framework.org/api-guide/viewsets/#viewset

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.

例如, 业务action被实现,

但是此类是继承于APIView,将 HTTP.METHOD映射到 VIEW.METHOD

并没有将HTTP.METHOD映射到 MIXIN.ACTION

from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet

class ItemViewSet(ViewSet):
    queryset = Item.objects.all()

    def list(self, request):
        serializer = ItemSerializer(self.queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        item = get_object_or_404(self.queryset, pk=pk)
        serializer = ItemSerializer(item)
        return Response(serializer.data)

Handling URLs

ViewSet注册URL方式,使用 两种类型的 router。

https://testdriven.io/blog/drf-views-part-3/#handling-urls

Instead of using Django's urlpatterns, ViewSets come with a router class that automatically generates the URL configurations.

DRF comes with two routers out-of-the-box:

  1. SimpleRouter
  2. DefaultRouter

The main difference between them is that DefaultRouter includes a default API root view:

DRF Browsable API

The default API root view lists hyperlinked list views, which makes navigating through your application easier.

例如:

# urls.py

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

from .views import ChangeUserInfo, ItemsViewSet

router = routers.DefaultRouter()
router.register(r'custom-viewset', ItemsViewSet)

urlpatterns = [
    path('change-user-info', ChangeUserInfo.as_view()),
    path('', include(router.urls)),
]

GenericViewSet --> GenericAPIView, ViewSetMixin

GenericAPIView 继承于 APIView,

  同时设计了 面向数据表的 控制属性,

GenericViewSet 继承了 GenericAPIView

  同时也继承了ViewSetMixin,将HTTP.METHOD映射到MIXIN.ACTION,子类(业务类)则需要实现ACTION。

https://www.django-rest-framework.org/api-guide/viewsets/#genericviewset

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.

显示实现ACTION

例如 list create ...

https://testdriven.io/blog/drf-views-part-3/#genericviewset

from rest_framework import status
from rest_framework.permissions import DjangoObjectPermissions
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

class ItemViewSet(GenericViewSet):
    serializer_class = ItemSerializer
    queryset = Item.objects.all()
    permission_classes = [DjangoObjectPermissions]

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save(serializer)

        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def list(self, request):
        serializer = self.get_serializer(self.get_queryset(), many=True)
        return self.get_paginated_response(self.paginate_queryset(serializer.data))

    def retrieve(self, request, pk):
        item = self.get_object()
        serializer = self.get_serializer(item)
        return Response(serializer.data)

    def destroy(self, request):
        item = self.get_object()
        item.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

直接继承MIXINS

将mixins中实现过的 MIXINS 继承过来。

https://testdriven.io/blog/drf-views-part-3/#genericviewset

from rest_framework import mixins, viewsets

class ItemViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

ModelViewSet --> GenericViewSet, Mixins

GenericViewSet 的缺点是, 需要自行实现 action,

对于通用性(只需要实现数据库表操作)的场景, action都是一样的,

此类将所有的 Mixins类继承过来, 则所有的CRUD操作都已经支持。

https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset

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().

对于一个数据库表, 只需要做如下声明, 即支持了所有的CRUD

class ItemModelViewSet(ModelViewSet):
    serializer_class = ItemSerializer
    queryset = Item.objects.all()

路由注册到URL

# urls.py

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

from .views import ChangeUserInfo, ItemsViewSet, ItemModelViewSet

router = routers.DefaultRouter()
router.register(r'custom-viewset', ItemsViewSet)
router.register(r'model-viewset', ItemModelViewSet) # newly registered ViewSet

urlpatterns = [
    path('change-user-info', ChangeUserInfo.as_view()),
    path('', include(router.urls)),
]

ReadOnlyModelViewSet --> GenericViewSet, Mixins(list retrieve)

只提供读取的 ACTION.

https://www.django-rest-framework.org/api-guide/viewsets/#readonlymodelviewset

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().

非写接口。

from rest_framework.viewsets import ReadOnlyModelViewSet

class ItemReadOnlyViewSet(ReadOnlyModelViewSet):

    serializer_class = ItemSerializer
    queryset = Item.objects.all()

https://img2020.cnblogs.com/blog/603834/202110/603834-20211012235850083-113696365.png

 

出处:http://www.cnblogs.com/lightsong/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
原文地址:https://www.cnblogs.com/lightsong/p/15418867.html