zoukankan      html  css  js  c++  java
  • RestFramework之视图组件

    一、视图组件的使用

      在我们自己书写视图类时需要不断书写重复冗余的代码,看起来十分繁琐不简洁易见,当然rest_framework中的视图组件帮我们做到了一些必要的步骤,使我们节省了编写冗余代码的时间。

      1.视图组件的导入

    from rest_framework import generics     # generics.GenericAPIView 里面封装了APIView
    from rest_framework.mixins import (
        ListModelMixin,                     # ListModelMixin表示get请求,获取全部数据
        CreateModelMixin,                   # CreateModelMixin表示post请求,添加数据
        DestroyModelMixin,                  # DestroyModelMixin表示delete请求,删除数据
        UpdateModelMixin,                   # UpdataModelMixin表示put请求,修改数据
        RetrieveModelMixin                  # RetrieveModelMixin表示get请求,获取单条数据
    )

      2.视图组件的使用 

    # 视图组件优化第一版
    class BookListView(ListModelMixin, CreateModelMixin, generics.GenericAPIView):
        '''
        使用组件的步骤:
            1.获取queryset对象
            2.绑定序列化类
        '''
        queryset = Book.objects.all()      
        serializer_class = BookSerializer
    
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, generics.GenericAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, *args, **kwargs):
            # print(self.kwargs) {'pk': '1'}
            return self.retrieve(request, *args, **kwargs)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)

      是不是觉得视图组件帮我们做到了很多麻烦的事,还没完下面是改进版,

    # 第二版本
    class BookView(generics.ListCreateAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
    
    
    class BookFilterView(generics.RetrieveUpdateDestroyAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer

      不用我们自己写每个请求的方法了,去看看源码:其实是源码帮我们写了 我们要写的了。

    class ListCreateAPIView(mixins.ListModelMixin,
                            mixins.CreateModelMixin,
                            GenericAPIView):
        """
        Concrete view for listing a queryset or creating a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    
    
    
    class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
                                mixins.UpdateModelMixin,
                                GenericAPIView):
        """
        Concrete view for retrieving, updating a model instance.
        """
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    
        def patch(self, request, *args, **kwargs):
            return self.partial_update(request, *args, **kwargs)

      但是,我们还是要写两个这个:

        queryset = Book.objects.all()
        serializer_class = BookSerializer

    最终版本:

      

      在使用时需要注意url中需要对每个请求类型声明

      url:

     re_path(r'book/$', views.BookView.as_view({
            'get': 'list',
            'post': 'create'
        })),
        re_path(r'book/(?P<pk>d+)/$', views.BookView.as_view({
            'get': 'retrieve',
            'put': 'update',
            'delete': 'destroy'
        }))
        # 注:在使用ModelViewSet时需要在as_view()中声明请求对应的视图方法,如:get请求对应list ( ListModelMixin )
    ]

      导入与使用:

    from rest_framework.viewsets import ModelViewSet            #ModelViewSet 里面既封装了mixins又封装了generics中的方法
    # 终极版
    class BookView(ModelViewSet):
        queryset = Book.objects.all()
        serializer_class = BookSerializer

     只需要导入ModelViewSet就可以解决我们麻烦的冗余代码,看看源码怎么帮我们解决的:  

      第一步: ModelViewSet类, 继承了放有具体执行每种操作的类(list, create, update, destroy, retrieve)

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass

      第二步: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

      第三步: ViewSetMixin

    class ViewSetMixin(object):
        """
        This is the magic.
    
        Overrides `.as_view()` so that it takes an `actions` keyword that performs
        the binding of HTTP methods to actions on the Resource.
    
        For example, to create a concrete view binding the 'GET' and 'POST' methods
        to the 'list' and 'create' actions...
    
        view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
        """
    
        @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 suffix initkwarg is reserved for displaying the viewset type.
            # 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))
    
            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
                for method, action in actions.items():    # actions ---> {"get": "list", "post": "create"}   {"put": "update", "delete": "destroy", "get": "retrieve"}
                    handler = getattr(self, action) # 第一种get请求  self.list = getattr(self, list)  第二种 get请求  self.retrieve = getattr(self, retrieve)
                    setattr(self, method, handler)  # 第一种get请求 self.get = self.list  第二种get请求  self.get = self.retrieve
    
                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
                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.suffix = initkwargs.get('suffix', None)
            view.actions = actions
            return csrf_exempt(view)

      这个类里面有as_view(cls, actions=None, **initkwargs)方法, 也就是我们的url里面写的as_view({"get": "list", "post": "create"}),执行的是这个类中的as_view()方法,而不是APIView种的as_view()方法了!

    把那个参数字典传给actions,在放回的view函数中,把每种请求要执行的那种方法 setattr()设置了(看代码中注释);

    然后当用户来访问时,执行那个view函数,正常执行dispatch()方法,根据请求方式分发到每种请求的方法中,这次这个方法,是使用setattr()方法设置给self的,没有具体写,也就能正常执行每种方法了。

      

  • 相关阅读:
    更改ubuntu的官方镜像源
    python中的decorator的作用
    thunderbird 设置 邮件回复时内容在上方显示
    Sapnco3 RfcTable Structure
    DbEntry 访问Access2010数据库
    DbEntry 默认 主键ID为long
    DbEntry 简单实现
    nginx学习时使用EditPuls编辑conf.xml
    java的集合类面试题
    tomcat组成介绍和调优方案
  • 原文地址:https://www.cnblogs.com/qq631243523/p/10084276.html
Copyright © 2011-2022 走看看