drf : 通用檢視類和(GenericAPIView)5個檢視擴充套件類,九個檢視子類,檢視集。

冀未然發表於2024-03-25

檢視

REST framework 提供了眾多的通用檢視基類與擴充套件類,以簡化檢視的編寫。

APIView

rest_framework.views.APIView

APIViewREST framework提供的所有檢視的基類,繼承自Django的View父類。

GenericAPIView使用[通用檢視類]

繼承自APIVIew,主要增加了操作序列化器和資料庫查詢的方法,作用是為下面Mixin擴充套件類的執行提供方法支援。通常在使用時,可搭配一個或多個Mixin擴充套件類。

點選第一層檢視程式碼

class BookView(APIView):
    renderer_classes = [JSONRenderer]

    # 建立資料反序列化
    def post(self, request):
        ser = BookSerializers(data=request.data)
        if ser.is_valid():
            ser.save()
        return Response(ser.data)

    # 讀取序列化
    def get(self, request):
        book_list = Books.objects.all()
        # many=True 代表序列化多條資料
        ser = BookSerializers(instance=book_list, many=True)

        response = {'code': 100, 'msg': '查詢成功', 'result': ser.data}
        return Response(response, status=status.HTTP_202_ACCEPTED, headers={'name': 'junjie'})


# 查詢單條
class BookDetailView(APIView):
    def get(self, request, pk):
        book = Books.objects.filter(pk=pk).first()
        ser = BookSerializers(instance=book)
        return Response(ser.data)

    def delete(self, request, pk):
        Books.objects.filter(pk=pk).delete()
        return Response()

    # 修改
    def put(self, request, pk):
        book = Books.objects.filter(pk=pk).first()
        ser = BookSerializers(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)

GenericAPIView[通用檢視類]的方法屬性以及方法,第二層的演變。

* 屬性
* serializer_class指明檢視使用的序列化器
* 方法
* get_serializer_class(self)

現在來想繼承GenericAPIView來寫檢視函式改如何寫?先檢視GenericAPIView類定義了什麼屬性和方法。

圖書的五個介面,第二層,精簡模型類和序列化類

from .models import Books, Publish
from rest_framework.generics import GenericAPIView
from .serizlizer import BookSerializers, PublishSerializers
from rest_framework.response import Response


class BookAPIView(GenericAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers

    def get(self, request):
        book_list = self.get_queryset()
        ser = self.get_serializer(instance=book_list, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)


class BookDetailView(GenericAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers

    def get(self, request, pk):
        book_list = self.get_object()
        ser = self.get_serializer(instance=book_list)
        return Response(ser.data)

    def put(self, request, pk):
        book_obj = self.get_object()
        # 得到序列化類物件,傳入單條物件和要修改的資料欄位
        ser = self.get_serializer(instance=book_obj, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)

    def delete(self, request, pk):
        book = self.get_object()
        book.delete()
        return Response()

如果想要再寫一個檢視類只需要繼承GenericAPIView修改queryset和serializer_class即可,那麼有沒有別的方法可以節省程式碼?

使用GenericAPIView+5個檢視擴充套件類

from rest_framework.mixins import 
CreateModelMixin, # 建立單個
ListModelMixin, # 查詢所有
DestroyModelMixin, # 刪除單個 碟嘶踹
UpdateModelMixin, # 新建單個
RetrieveModelMixin # 查詢單個 瑞吹霧

程式碼如下:

from rest_framework.mixins import CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, \
    ListModelMixin
from rest_framework.generics import GenericAPIView
from .models import Books, Publish
from .serizlizer import BookSerializers, PublishSerializers


class BookAPIView(GenericAPIView,
                  # 建立資料                  
                  CreateModelMixin,
                  # 檢視所有
                  ListModelMixin):
    queryset = Books.objects.all()
    serializer_class = BookSerializers

    def get(self, request):
        print(request)
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookDetailView(GenericAPIView,
                     UpdateModelMixin,    # 修改單條資料
                     DestroyModelMixin,   # 刪除單條
                     RetrieveModelMixin): # 查詢單條
    queryset = Books.objects.all()
    serializer_class = BookSerializers

    def get(self, request, pk):
        print(request)
        return self.retrieve(request, pk)

    def delete(self, request, pk):
        print(request)
        return self.destroy(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)


為什麼叫檢視擴充套件類:因為他不是檢視類,檢視類必須是繼承View/APIView。

還有沒有辦法再精簡程式碼?

GenericAPIView的檢視子類,第三層

1)CreateAPIView

提供 post 方法

繼承自: GenericAPIView、CreateModelMixin

2)ListAPIView

提供 get 方法

繼承自:GenericAPIView、ListModelMixin

3)RetrieveAPIView

提供 get 方法

繼承自: GenericAPIView、RetrieveModelMixin

4)DestoryAPIView

提供 delete 方法

繼承自:GenericAPIView、DestoryModelMixin

5)UpdateAPIView

提供 put 和 patch 方法

繼承自:GenericAPIView、UpdateModelMixin

以及:

RetrieveAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView
from rest_framework.generics import CreateAPIView, UpdateAPIView, DestroyAPIView, ListAPIView, RetrieveAPIView, \
    ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView


class BookAPIView(ListCreateAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers


class BookDetailAPIView(RetrieveUpdateDestroyAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializers

總結:

#兩個基類
APIView
GenericAPIView:有關資料庫操作,queryset 和serializer_class


#5個檢視擴充套件類(rest_framework.mixins)
CreateModelMixin:create方法建立一條
DestroyModelMixin:destory方法刪除一條
ListModelMixin:list方法獲取所有
RetrieveModelMixin:retrieve獲取一條
UpdateModelMixin:update修改一條

#9個子類檢視(rest_framework.generics)
CreateAPIView:繼承CreateModelMixin,GenericAPIView,有post方法,新增資料
DestroyAPIView:繼承DestroyModelMixin,GenericAPIView,有delete方法,刪除資料
ListAPIView:繼承ListModelMixin,GenericAPIView,有get方法獲取所有
UpdateAPIView:繼承UpdateModelMixin,GenericAPIView,有put和patch方法,修改資料
RetrieveAPIView:繼承RetrieveModelMixin,GenericAPIView,有get方法,獲取一條


ListCreateAPIView:繼承ListModelMixin,CreateModelMixin,GenericAPIView,有get獲取所有,post方法新增
RetrieveDestroyAPIView:繼承RetrieveModelMixin,DestroyModelMixin,GenericAPIView,有get方法獲取一條,delete方法刪除
RetrieveUpdateAPIView:繼承RetrieveModelMixin,UpdateModelMixin,GenericAPIView,有get獲取一條,put,patch修改
RetrieveUpdateDestroyAPIView:繼承RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView,有get獲取一條,put,patch修改,delete刪除

第五層,直接寫五個介面最終演示,自動生成路由。

from rest_framework.viewsets import ModelViewSet

# ModelViewSet繼承了5個檢視擴充套件類,GenericViewSet 和 ViewSetMixin
class BookAPIView(ModelViewSet):
    queryset = Books.objects.all()
    serializer_class = BookSerializers

繼承五個檢視擴充套件類的作用為用了五個介面方法。

GenericViewSet是什麼?

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
    """
    The GenericViewSet class does not provide any actions by default,
    but does include the base set of generic view behavior, such as
    the `get_object` and `get_queryset` methods.
    """
    pass
  
  
"""
已知:generics.GenericAPIView 中有View和APIView,以及四個方法
    def get_queryset(self):
       
    def get_object(self):
      
    def get_serializer(self, *args, **kwargs):
    
    def get_serializer_class(self):
"""

ViewSetMixin

class ViewSetMixin:

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):

        cls.name = None
        cls.description = None

        cls.suffix = None

        cls.detail = None

        cls.basename = None

        # 註釋:
				"""
				此處與url相對應,actions是什麼?
				path('books/', views.BookAPIView.as_view({'get':'list','post':'create'})),
				actions = {'get':'list','post':'create'}
				如果沒有寫則報異常。
				"""
        if not actions:
            raise TypeError("The `actions` argument must be provided when "
                            "calling `.as_view()` on a ViewSet. For example "
                            "`.as_view({'get': 'list'})`")
            # 註釋:
  	    """
  	    字典.items()是將K,V拆出,此時method=K,action=V
            以{'get':'list','post':'create'}舉例。
  	    get = method,list = action
  	    """
            for method, action in actions.items():
              	# 註釋:
                """
                此時的self為檢視類物件BookAPIView
                action是上述所講的url,as_view的第一個引數。
                
                getattr反射,此時檢視類物件中沒有list,到父類ModelViewSet --> 
                mixins.ListModelMixin -->  def list(self, request, *args, **kwargs):
                
                handler 便是反射出來的list
                
                setattr設定值,將list對映成get,檢視中的get方法實質上是list方法。
                客戶端傳送get請求就能找到list
                這裡主要用作區分使用者是查詢單條還是查詢所有
                """
                handler = getattr(self, action)
                setattr(self, method, handler)

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            return self.dispatch(request, *args, **kwargs)

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())

        # We need to set these on the view function, so that breadcrumb
        # generation can pick out these bits of information from a
        # resolved URL.
        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view)

第五層

匯入模組

from rest_framework.viewsets import ModelViewSet

直接寫五個介面最終演示,自動生成路由。

from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, GenericViewSet, ViewSet


# 直接寫五個介面,一定要繼承(ViewSetMixin),才能自動生成路由

# class BookAPIView(ModelViewSet):
#     queryset = Books.objects.all()
#     serializer_class = BookSerializers

# 查詢所有和查詢單個,如果只繼承了ReadOnlyModelViewSet,路由也需要更改。
# class BookAPIView(ReadOnlyModelViewSet):
#     queryset = Books.objects.all()
#     serializer_class = BookSerializers


# GenericViewSet:繼承了(ViewSetMixin, generics.GenericAPIView)

# ViewSet:繼承了(ViewSetMixin, views.APIView)


class BookAPIView(ViewSet):
    def xjz(self, request):
        return Response("junjie")

url

# 自動生成路由
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
# 必須繼承(ViewSetMixin),才能自動生成路由
router.register('books', views.BookAPIView)

urlpatterns = [
    path('admin/', admin.site.urls),

    # 將請求對映到對應的方法
    path('books/', views.BookAPIView.as_view({'get':'list'})),
    path('books/<int:pk>', views.BookAPIView.as_view({'get':'retrieve'})),
]

urlpatterns += router.urls

匯入模組

from rest_framework.routers import SimpleRouter
from django.urls import path,include
router = SimpleRouter()
# 必須繼承(ViewSetMixin),才能自動生成路由
router.register('admin', views.BookAPIView)

urlpatterns = [
    # path('admin/', admin.site.urls),

    path('api/',include(router.urls))
]

此時瀏覽器訪問admin的路由:http://127.0.0.1:8000/api/admin/

相關文章