zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    postman设置页面详解
    postman安装使用
    测试入门1:黑盒测试用例设计方法
    oo第十六次作业
    oo第三单元总结
    OO第二单元总结
    select语句
    MySQL数据库基础操作
    创建和查看数据库
    认识MySQL数据库
  • 原文地址:https://www.cnblogs.com/williamweson/p/13052584.html
Copyright © 2011-2022 走看看