drf中View和router的詳解


Rest Framework 視圖和路由

因為涉及到視圖層面了,而且下面的例子會反復用到request.data,所以我決定帶大家稍微看下源碼,感興趣的可以自己深入了解

無論是View還是APIView最開始都是調用as_view()

大致過了下APIView給我們封裝的數據

總結一下

  • 舊的request封裝到新request屬性_request里
  • 繼承APIView,重新封裝的request.query_params相當於舊的request.GET
  • request.data相當於舊的request.POST和request.FILES,且支持json數據類型

第一版封裝

app/views

class BookView(APIView):
    def get(self, request):
        query_set = Book.objects.all()
        book_ser = BookSerializer(query_set, many=True)
        return Response(book_ser.data)

    def post(self, request):
        query_set = request.data
        book_ser = BookSerializer(data=query_set)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)
        
class BookEditView(APIView):     
    def get(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(query_set)
        return Response(book_ser.data)

    def patch(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        book_ser = BookSerializer(query_set, data=request.data, partial=True)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.validated_data)
        else:
            return Response(book_ser.errors)

    def delete(self, request, pk):
        query_set = Book.objects.filter(pk=pk).first()
        if query_set:
            query_set.delete()
            return Response("")
        else:
            return Response("刪除的書籍不存在")

使用Mixin封裝方法

class GenericAPIView(APIView):
    queryset = None
    serializer_class = None

    def get_queryset(self):
        return self.queryset.all()

    def get_serializer(self,*args,**kwargs):
        return self.serializer_class(*args,**kwargs)
class ListModelMixin:

    def list(self,request,*args,**kwargs):
        queryset = self.get_queryset()
        book_sel = self.get_serializer(queryset,many=True)
        return Response(book_sel.data)

class CreateModelMixin:

    def create(self,request,*args,**kwargs):
        book_sel = self.serializer_class(data=request.data)
        if book_sel.is_valid():
            book_sel.save()
            return Response(book_sel.data)
        else:
            return Response(book_sel.errors)

class UpdateModelMixin:

    def update(self,request,pk,*args,**kwargs):
        book_obj = self.get_queryset().filter(pk=pk).first()
        book_sel = self.serializer_class(book_obj,data=request.data,partial=True)
        if book_sel.is_valid():
            book_sel.save()
            return Response(book_sel.data)
        else:
            return Response(book_sel.errors)


class RetrieveModelMixin:

    def retrieve(self,request,pk,*args,**kwargs):
        book_obj = self.get_queryset().filter(pk=pk).first()
        book_sel = self.serializer_class(book_obj)
        return Response(book_sel.data)

class DestroyModelMixin:
    def destroy(self, request, pk, *args, **kwargs):
        queryset = self.get_queryset()
        try:
            queryset.get(pk=pk).delete()
            return Response("")
        except Exception as e:
            return Response("信息有誤")

# Create your views here.
class BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,pk,*args,**kwargs):
        return self.retrieve(request,pk,*args,**kwargs)

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

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


class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

    def post(self,request,*args,**kwargs):
        return self.create(request,*args,**kwargs)
    
# 技術點:因為drf中的GenericAPIView提供了queryset和serializer_class,如果要繼承GenericAPIView,
# 則必須重寫這兩個字段,且GenericAPIView提供get_queryset和get_serializer兩個方法
# Mixin類不用繼承其他API,只是單獨提供方法接口,必須跟其他API類混合繼承

感覺經過這么一封裝,每個類中的方法看起來清爽多了,我們還可以繼續封裝

第二版封裝

# 上面我們寫的繼承類太長了~~我們再改改

class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
    pass


class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    pass


class BookEditView(RetrieveUpdateDestroyAPIView):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,pk,*args,**kwargs):
        return self.retrieve(request,pk,*args,**kwargs)

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

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


class BookView(ListCreateAPIView):
    queryset = models.Book.objects
    serializer_class = BookSerializers
    def get(self,request,*args,**kwargs):
        return self.list(request,*args,**kwargs)

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

感覺只是把類中的繼承稍微簡化了下,並不是特別的優雅,來康康第三版

第三版封裝

我們知道,一般的View執行as_view()不能傳入參數,接下來要介紹的ViewSetMixin,重寫了as_view(actions),可以傳入我們需要的參數

urlpatterns = [
    url(r'^book$', BookView.as_view({"get": "list", "post": "create"})),
    url(r'^retrieve/(?P<pk>\d+)$', BookEditView.as_view({"get": "retrieve", "patch": "update", "delete": "destroy"})), //這里要注意的是,使用這種傳參的view,傳入的動態id要命名為pk
]

urls.py
from rest_framework.viewsets import ViewSetMixin


# class BookView(ViewSetMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
#     queryset = Book.objects.all()
#     serializer_class = BookSerializer
    
    
# 如果我們再定義一個類
class ModelViewSet(ViewSetMixin, ListCreateAPIView):
    pass
class OwnViewSet(ViewSetMixin,RetrieveUpdateDestroyAPIView)

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
class BookEditView(OwnViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

我們現在的視圖就只要寫兩行就可以了,其實我們寫的所有的視圖~框架都幫我們封裝好了,剛剛上面用的例子都是手動封裝

奉獻一張圖來看下我們的繼承順序~~~

img

drf的路由

我們上面的路由傳參寫的特別多~~框架也幫我們封裝好了~

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r"book", BookView)

urlpatterns = [

]
urlpatterns += router.urls

通過框架的路由可以看出,手寫的代碼幾乎沒有了,這里提出一點建議,如果自己的業務邏輯不是跟增刪改查特別耦合

不建議用drf提供的路由組件,因為這樣會暴露很多的接口,不太安全,總之,一般我們很少用到這個組件,還是盡量自己手寫

總結

類的繼承鏈越高,所擁有的功能也就越少,可定制化的程度就越高,盡管上面我們用底層的類,特別輕松的實現了功能,

但需要自定制時,還是繼承APIView實現自己的業務邏輯,總之一切按照業務邏輯來走

參考鏈接

https://www.cnblogs.com/GGGG-XXXX/articles/9675911.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM