• rest-framework视图组件(views)


    一:APIView和View的区别

        a:rest-framework对原Request进行了封装

          --request.query_params 存放的是我们get请求的参数

          --request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求

    二:对Views视图进行封装

         a:观察几个请求对应的方法的共同之处

          1:models.Book.objects.all()或者models.Book.objects.filter(**kwargs)

          2:book_serializers = BookSerializers(book_obj,data=request.data,partial=True)      #参数不一样

        def put(self,request,id):
            print(request.data)
            book_obj = Book.objects.filter(id=id).first()
            # partial   True 表示可以进行部分字段跟新,Flase 表示全部跟新,默认False
            book_serializers = BookSerializers(book_obj,data=request.data,partial=True)
    
            #需要重写 updata
            if book_serializers.is_valid():
                book_serializers.save()
                return Response(book_serializers.validated_data)
            else:
                return Response(book_serializers.errors)
    
    
        def  delete(self,request,id):
            book_obj = Book.objects.filter(id=id).first()
            book_obj.delete()
            return  Response("")
    

        3:进行初次封装

           注意点:
               i:定义序列化和反序列化类 并进行设置 serializer_class = BookSerializers

              II:获取到对应的对象(可以时多个也可以是单个)query_set = models.Book.objects.all()

             III:创建一个类 GenericApIview 将上面两个属性进行通过方法获取

             IV:Mixin类对不同的方法进行封装。并因为里面没有 query_set和serializer_class属性。所以需要和GenericApiView类进行混合继承。

    from  SerDemo import models
    from  rest_framework.views import APIView     #视图函数继承APIView  不是View
    from rest_framework.response import Response  #用于返回值
    #BookSerializers  自己定义的序列化规则
    from SerDemo.serializers import *
    
    class  GenericAPIView(APIView):
        query_set = None
        serializer_class = None
        def   get_query_set(self):
            return self.query_set
    
        def   get_serializer_class(self,*args,**kwargs):
            return self.serializer_class(*args,**kwargs)
    
    class ListModelMixin(object):
        def list(self, request, *args, **kwargs):
            book_obj = self.get_query_set()
            # book_obj = models.Book.objects.all()
            # 序列化多组数据 和一个对象,需要many=TRUE  默认一个
            ret = self.get_serializer_class(book_obj, many=True)
            # ret  =BookSerializers(book_obj,many=True)
            # 序列化的数据在data属性中
            return Response(ret.data)
    
    class CreateModelMixin(object):
        def  create(self,request,*args,**kwargs):
            data = self.get_serializer_class(data=request.data)
            # 进行数据验证,并且在BookSerializers中重写create方法
            if data.is_valid():
                data.save()
                return Response(data.data)
            else:
                return Response(data.errors)
    class RetrieveModelMixin(object):
        def  retrieve(self,id,request,*args,**kwargs):
            book_obj = self.get_query_set().filter(id=id).first()
            book = self.get_serializer_class(book_obj)
            return Response(book.data)
    
    class UpdateModelMixin(object):
        def update(self, request, id, *args, **kwargs):
            book_obj = self.get_query_set().filter(pk=id).first()
            book_ser = self.get_serializer(book_obj, 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)
    
    
    class DestroyModelMixin(object):
        def destroy(self, request, id, *args, **kwargs):
            queryset = self.get_query_set()
            try:
                queryset.get(pk=id).delete()
                return Response("")
            except Exception as e:
                return Response("信息有误")
    
    class BookApiView(GenericAPIView,ListModelMixin,CreateModelMixin):
        query_set=models.Book.objects.all()
        serializer_class=BookSerializers
        def  get(self,request,*args,**kwargs):
    
            # book_obj =self.get_query_set()
            # book_obj = models.Book.objects.all()
            #序列化多组数据 和一个对象,需要many=TRUE  默认一个
            # ret  =  self.get_serializer_class(book_obj,many=True)
            # ret  =BookSerializers(book_obj,many=True)
            #序列化的数据在data属性中
            return  self.list(request, *args, **kwargs)
    
        def post(self,request,*args,**kwargs):
            return self.create(request,*args,**kwargs)
    
    class   BookEditView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
        query_set = models.Book.objects.all()
        serializer_class = BookSerializers
        def  get(self, request,id,*args,**kwargs):
            return  self.retrive(id,request,*args,**kwargs)
    
        def put(self,request,id,*args,**kwargs):
            return self.update(request,id,*args,**kwargs)
    
    
        def  delete(self,request,id,*args,**kwargs):
            return self.destroy(request, id, *args, **kwargs)
    

      4:再次优化封装

            现在有两个问题

              一:view中有两个方法。能不能合成一个

              二:对应的View视图函数中只有对应的方法调用,不存在任何逻辑。能否通过url映射取出对应的方法

           a:修改url

             --观察上面修改的方法,可以看出link/ 路径下  get ---调用类了 self.list  post调用了 self.create方法

             --可以得出结论 如果我们按下面袖该的url分发并执行对应的函数,那么我们就可以进一步简化

             --但是原有的方法as_view方法不能进行传参数

    urlpatterns = [
        path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
        path('retrieve/<int:id>',
             views.BookModelSetView.as_view({"get":"retrieve","put":"update","delete":"destroy"}))
    
    ]

           b:解决as_view()方法传参

             i:导入ViewSetMixin类

    from rest_framework.viewsets import ViewSetMixin
    

            II:阅读对应的源码:

               --as_view()进行重写,然后看view方法(不建议改动源码,我是为了学习注释。)

    @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            """
            Because of the way class based views create a closure around the
            instantiated view, we need to totally reimplement `.as_view`,
            and slightly modify the view function that is created and returned.
            """
            # The name and description initkwargs may be explicitly overridden for
            # certain route confiugurations. eg, names of extra actions.
            cls.name = None
            cls.description = None
    
            # The suffix initkwarg is reserved for displaying the viewset type.
            # This initkwarg should have no effect if the name is provided.
            # eg. 'List' or 'Instance'.
            cls.suffix = None
    
            # The detail initkwarg is reserved for introspecting the viewset type.
            cls.detail = None
    
            # Setting a basename allows a view to reverse its action urls. This
            # value is provided by the router through the initkwargs.
            cls.basename = None
    
            # actions must not be empty
            if not actions:
                raise TypeError("The `actions` argument must be provided when "
                                "calling `.as_view()` on a ViewSet. For example "
                                "`.as_view({'get': 'list'})`")
    
            # sanitize keyword arguments
            for key in initkwargs:
                if key in cls.http_method_names:
                    raise TypeError("You tried to pass in the %s method name as a "
                                    "keyword argument to %s(). Don't do that."
                                    % (key, cls.__name__))
                if not hasattr(cls, key):
                    raise TypeError("%s() received an invalid keyword %r" % (
                        cls.__name__, key))
    
            # name and suffix are mutually exclusive
            if 'name' in initkwargs and 'suffix' in initkwargs:
                raise TypeError("%s() received both `name` and `suffix`, which are "
                                "mutually exclusive arguments." % (cls.__name__))
    
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                # We also store the mapping of request methods to actions,
                # so that we can later set the action attribute.
                # eg. `self.action = 'list'` on an incoming GET request.
                self.action_map = actions
    
                # Bind methods to actions
                # This is the bit that's different to a standard view
                '''
                    1:actions={"get":"list","post":"create"}   as_view()方法中传递的参数
                    2:循环后结果为
                        methon ="get"      #key
                        action ='list'     #对应的方法字符串
                        
                '''
                for method, action in actions.items():
                    '''
                     3:通过getattr将字符串反射对应的方法名
                        handler = list    #循环将每一个字符传,修改对应的放啊
                    
                     4:通过setattr(self, method, handler) 得到的结果为
                       set.method =self.handler -->self.get=self.list.
                     5:成功映射对应的方法  
                        
                    '''
                    handler = getattr(self, action)
    
                    setattr(self, method, handler)
    
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
    
                self.request = request
                self.args = args
                self.kwargs = kwargs
    
                # And continue as usual
                '''
                  6:重新进行路由分发,并且已经修改成功方法
                '''
                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)
    

        c:最后进行view编写:

    from rest_framework.viewsets import ViewSetMixin
    
    # 定义一个类,用来简化BookModelSetView的继承类的长度,不是很重要
    class ModelViewSet(ViewSetMixin,GenericAPIView,RetrieveModelMixin,
                            UpdateModelMixin,DestroyModelMixin,CreateModelMixin,ListModelMixin):
        pass
    class  BookModelSetView(ModelViewSet):
        query_set = models.Book.objects.all()
        serializer_class = BookSerializers
    

      5:使用rest_framework自带的方法实现以上功能

              --导入 from  rest_framework.viewsets import ModelViewSet

              --参数是queryset和serializer_class

              --路由修改id 成pk

              --该方法和上面自修改方法本质上相同。

              --使用自带的方法简便,但是有时候并不需要那么多的方法,所以自定义方法可以由于简化请求方法。

    from  rest_framework.viewsets import ModelViewSet
    class  BookModelSetView(ModelViewSet):
        queryset = models.Book.objects.all()
        serializer_class = BookSerializers
    
    urlpatterns = [
        path('link/', views.BookModelSetView.as_view({"get":"list","post":"create"})),
        path('retrieve/<int:pk>',
             views.BookModelSetView.as_view({"get":"retrive","put":"update","delect":"destroy"}))
    
    ]  

    三:小结

          1:以上用到的方法均在以下4个类中,可以查看下源码

            from rest_framework import views,generics,mixins,viewsets

       

    四:路由系统

        了解下即可,最好不要用这种方法

    from .views import BookView
    from rest_framework.routers import DefaultRouter
    
    router = DefaultRouter()
    router.register(r"book", BookView)
    
    urlpatterns = [
    
    ]
    urlpatterns += router.urls
    

      

      

           

          

  • 相关阅读:
    spring mvc 参数校验
    spring-boot 配置jsp
    java 多线程之取消与关闭
    spring transaction 初识
    java 读取环境变量和系统变量的方法
    每天一命令 git checkout
    每天一命令 git stash
    每天一命令 git reset
    log4j2配置详解
    专业名词及解释记录
  • 原文地址:https://www.cnblogs.com/yingjp/p/10604212.html
Copyright © 2020-2023  润新知