• rest-framework框架——视图三部曲


    一、mixins类编写视图

    1、配置url

    urlpatterns = [
        ...
        re_path(r'^authors/$', views.AuthorView.as_view(), name="author"),
        re_path(r'^authors/(?P<pk>d+)/$', views.AuthorDetailView.as_view(), name="detail_author")
    ]
    

    2、编写Author的序列化类

    /app01/serializer.py:

    class AuthorModelSerializers(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = "__all__"
    

    3、编写Author的视图

    # Author
    from rest_framework import mixins, generics
    
    
    class AuthorView(mixins.ListModelMixin,        # 扩展了列出查询集功能
                     mixins.CreateModelMixin,      # 扩展了创建和保存新模型实例功能
                     generics.GenericAPIView):     # 继承扩展了REST框架的APIView类,为标准列表和详细视图添加了常见的行为
        queryset = Author.objects.all()     # 配置queryset:告知这个类这次处理的数据
        serializer_class = AuthorModelSerializers     # 告知处理用到的序列化组件
    
        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 AuthorDetailView(mixins.RetrieveModelMixin,   # 扩展在响应中实现返回现有模型实例功能(获取单条数据)
                           mixins.DestroyModelMixin,    # 扩展现有模型实例的删除功能
                           mixins.UpdateModelMixin,     # 扩展更新和保存现有模型实例功能
                           generics.GenericAPIView):
        queryset = Author.objects.all()     # 配置queryset:告知这个类这次处理的数据
        serializer_class = AuthorModelSerializers     # 告知处理用到的序列化组件
    
        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 delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

      注意:

    (1)queryset和serializer_class变量

      这两个变量是必须的。queryset告知这个类这次处理的数据。serializer_class告知这个类数据处理用到的序列化组件。

    (2)五类mixins作用和对应的http方法

      

    (3)GenericAPIView

      这个类扩展了REST框架的APIView类,为标准列表和详细视图添加了常见的行为。

      提供的每个具体的通用视图都是通过把GenericAPIView与一个或多个mixin类进行组合来构建的。

    (4)测试验证

       

    二、基于mixins封装视图示例

    1、基本视图

    # 方式三:基于rest_framework框架实现序列化(pip install djangorestframework)
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .serializers import BookSerializer   # 自定义序列化类
    
    
    class BookView(APIView):
        query_set = Book.objects.all()        # 将query_set抽离
        serializer_class = BookSerializer     # 拿到序列化器
    
        def get(self, request):
            # 第一个图书对象
            # book_obj = Book.objects.first()
            # ret = BookSerializer(book_obj)
    
            # book_list = Book.objects.all()
            book_list = self.query_set
            # ret = BookSerializer(book_list, many=True)    # 使用序列化器序列化
            ret = self.serializer_class(book_list, many=True)
            """
            序列化的数据保存在ret.data中
            """
            return Response(ret.data)
        """
        得出来的结果会使用Django REST framework模板,在serializers.py中定制好序列化类后,显示效果如下所示:
        HTTP 200 OK
        Allow: GET, HEAD, OPTIONS
        Content-Type: application/json
        Vary: Accept
    
        [
            {
                "id": 1,
                "title": "python开发",
                "category": "Python",
                "pub_time": "2011-08-27",
                "publisher": {
                    "id": 1,
                    "title": "人民日报社"
                },
                "author": [
                    {
                        "id": 1,
                        "name": "xxx"
                    },
                    {
                        "id": 2,
                        "name": "sssxx"
                    }
                ]
            },
           ...
        ]
        """
    
        def post(self, request):
            print(request.data)
            serializer = BookSerializer(data=request.data)  # 序列化器校验前端传回来的数据
            if serializer.is_valid():
                serializer.save()   # 验证成功后保存数据库
                # 因为ModelSerializer的create方法不支持source的用法。因此必须还自定义一个create方法。
                return Response(serializer.validated_data)   # validated_data存放验证通过的数据
            else:
                return Response(serializer.errors)           # errors存放错误信息
    
        '''
        发送post请求接口设计
        POST /books/list
        {
            "title": "nodejs的使用教程",
            "w_category": "1",
            "pub_time": "2018-10-27",
            "publisher_id": 1,
            "author_list": [1,2,3]
        }
        '''
    
    
    class BookEditView(APIView):
        def get(self, request, id):
            """查看单条数据"""
            book_obj = Book.objects.filter(id=id).first()
            ret = BookSerializer(book_obj)
            return Response(ret.data)
    
        '''
        GET /books/retrieve/3
        {
            "id": 3,
            "title": "Linux开发",
            "category": "Linux",
            "pub_time": "2008-08-27",
            "publisher": {
                "id": 3,
                "title": "长江日报社"
            },
            "author": [
                {
                    "id": 1,
                    "name": "阿萨德"
                },
                {
                    "id": 3,
                    "name": "阿斯达"
                }
            ]
        }
        '''
    
        def put(self, request, id):
            """更新操作"""
            book_obj = Book.objects.filter(id=id).first()
            serializer = BookSerializer(
                book_obj,             # 待更新对象
                data=request.data,    # 要更新的数据
                partial=True          # 重点:进行部分验证和更新
            )
            if serializer.is_valid():
                serializer.save()     # 保存
                return Response(serializer.validated_data)   # 返回验证通过的数据
                # return Response(serializers.data)    # 返回所有数据
            else:
                return Response(serializer.errors)      # 返回验证错误的数据
    
        def delete(self, request, id):
            """删除操作"""
            book_obj = Book.objects.filter(id=id).first()
            book_obj.delete()
            return Response("")

    2、基于mixins封装视图

      升级:改写使用通用类,继承通用方法。

    class GenericAPIView(APIView):
        # 通用APIView模板类
        query_set = None
        serializer_class = None
    
        def get_queryset(self):
            return self.query_set
    
        def get_serializer(self, *args, **kwargs):
            return self.serializer_class(*args, **kwargs)   # 实例化,且接收所有的参数
    
    
    class BookView(GenericAPIView):
        # 以方法的形式调用获取
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            book_list = self.get_queryset()
            ret = self.get_serializer(book_list, many=True)
            return Response(ret.data)
    
        def post(self, request):
            serializer = BookSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)

    3、第一次封装

    class GenericAPIView(APIView):
        # 通用APIView模板类
        query_set = None
        serializer_class = None
    
        def get_queryset(self):
            return self.query_set
    
        def get_serializer(self, *args, **kwargs):
            return self.serializer_class(*args, **kwargs)   # 实例化,且接收所有的参数
    
    
    class ListModelMixin(object):
        def list(self, request):
            queryset = self.get_queryset()
            ret = self.get_serializer(queryset, many=True)
            return Response(ret.data)
    
    
    class CreateModelMixin(object):
        def create(self, request):
            serializer = self.get_serializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                return Response(serializer.data)
            else:
                return Response(serializer.errors)
    
    
    class RetrieveModelMixin(object):
        def retrieve(self, request, id):   # 查看单条数据
            book_obj = self.get_queryset().filter(id=id).first()
            ret = self.get_serializer(book_obj)
            return Response(ret.data)
    
    
    class UpdateModelMixin(object):
        def update(self, request, id):
            book_obj = self.get_queryset().filter(id=id).first()
            serializer = self.get_serializer(
                book_obj,  # 待更新对象
                data=request.data,  # 要更新的数据
                partial=True  # 重点:进行部分验证和更新
            )
            if serializer.is_valid():
                serializer.save()  # 保存
                return Response(serializer.validated_data)  # 返回验证通过的数据
                # return Response(serializers.data)    # 返回所有数据
            else:
                return Response(serializer.errors)  # 返回验证错误的数据
    
    
    class DestroyModelMixin(object):
        def delete(self, request, id):
            book_obj = self.get_queryset().filter(id=id).first()
            book_obj.delete()
            return Response("")
    
    class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):   # 一层封装
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):  # 一层封装
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, id):
            """查看单条数据"""
            return self.retrieve(request, id)
    
        def put(self, request, id):
            """更新操作"""
            return self.update(request, id)
    
        def delete(self, request, id):
            """删除操作"""
            return self.destroy(request, id)

    4、第二次封装

      进一步封装如下所示:

    # 二层封装
    class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
        pass
    
    
    class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        pass
    
    
    # class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):   # 一层封装
    class BookView(ListCreateAPIView):          # 使用二层封装
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    # class BookEditView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):  # 一层封装
    class BookEditView(RetrieveUpdateDestroyAPIView):     # 使用二层封装
        query_set = Book.objects.all()
        serializer_class = BookSerializer
    
        def get(self, request, id):
            """查看单条数据"""
            return self.retrieve(request, id)

    5、第三次封装

      经过两次封装后,剩下两个视图,但依然不够简洁。由于两个get请求的返回值不同,写了两个视图来解决。

      现在可以通过路由传参的方式来解决上述问题。

    (1)改写urls.py

    from django.urls import path, include
    from .views import BookView, BookEditView, BookModelViewSet
    
    
    urlpatterns = [
        # path('list', BookView.as_view()),   # 查看所有的图书
        # 注意url中参数命名方式,2.0之前的写法:'retrieve/(?P<id>d+)'
        # 2.0之后的写法:<>内声明类型,冒号后面跟着关键字参数
        # path('retrieve/<int:id>', BookEditView.as_view())   # 单条数据查看
    
        path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
        path('retrieve/<int:id>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))
    ]

    (2)使as_view支持传参

      as_view方法本身是不支持传参的,因此需要重写该方法。

    class ViewSetMixin(object):
        def as_view(self):
            """
            按照参数指定的去匹配
            get-->list
            :return:
            """
            pass
    
    class BookModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin,
                           RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        pass

      但是我们不用自己去定义该方法,可以直接使用框架已经提供好的ViewSetMixin:

    from rest_framework.viewsets import ViewSetMixin

      它做的主要事情就是重写as_view方法:

    class ViewSetMixin:
        @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            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
                # 这里actions是前面url中提供的字典参数{"get": "list", "post": "create"}
                for method, action in actions.items():
                    # 循环得到的method是get,action是list
                    handler = getattr(self, action)   # self是自己的视图类
                    setattr(self, method, handler)   # 在自己的视图类中找list方法
    
                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
                # 做分发:get-->self.get;post-->self.post
                return self.dispatch(request, *args, **kwargs)

      由于self.get已经等于self.list,分发get对应的是list。

    (3)视图改写

    from rest_framework.viewsets import ViewSetMixin
    
    class BookModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin,
                           RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        query_set = Book.objects.all()
        serializer_class = BookSerializer

      由于get去匹配时已经自动对应到了self.list方法。因此不再自定义视图方法。

    (4)简化继承

    from rest_framework.viewsets import ViewSetMixin
    
    class ModelViewSet(ViewSetMixin, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        pass
    
    class BookModelViewSet(ModelViewSet):
        query_set = Book.objects.all()
        serializer_class = BookSerializer

    三、Mixins源码分析

    1、CreateModelMixin

    class CreateModelMixin(object):
        """Create a model instance ==>创建一个实例"""
        def create(self, request, *args, **kwargs):
            # 获取相关serializer
            serializer = self.get_serializer(data=request.data)
            
            # 进行serializer的验证;raise_exception=True,一旦验证不通过,不再往下执行,直接引发异常
            serializer.is_valid(raise_exception=True)
            
            # 调用perform_create()方法,保存实例
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
        def perform_create(self, serializer):
        # 保存实例
            serializer.save()
    
        def get_success_headers(self, data):
            try:
                return {'Location': str(data[api_settings.URL_FIELD_NAME])}
            except (TypeError, KeyError):
                return {}
    

      注意:

    (1)perform_create( )对serializer直接进行save保存,当在一些情境下,需要对perform_create( )进行重写。

    (2)这个类的运行流程如下所示:

      

    2、ListModelMixin

    class ListModelMixin(object):
        """List a queryset.==> 列表页获取"""
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
            
            # 这是一个分页功能,如果在viewset中设置了pagination_class,那么这里就会起作用
            # 获取当前页的queryset,如果不存在分页,返回None
            page = self.paginate_queryset(queryset)
            
            if page is not None:
            # 分页不为空,那么不能简单的执行Response(serializer.data)
            # 还需要将相关的page信息序列化在进行响应
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
    

      ListModelMixin一般用来获取列表页,不需要重写方法。

    3、RetriveModelMixin

    class RetrieveModelMixin(object):
        """
        Retrieve a model instance.==> 获取某一个对象的具体信息
        """
        def retrieve(self, request, *args, **kwargs):
            # 一般访问的url都为/obj/id/这种新式
            # get_object()可以获取到这个id的对象
            # 注意在viewset中设置lookup_field获取重写get_object()方法可以指定id具体对象是什么~!
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    

    4、DestoryModelMixin

    class DestroyModelMixin(object):
        """
        Destroy a model instance.
        """
        def destroy(self, request, *args, **kwargs):
            instance = self.get_object()
            self.perform_destroy(instance)
            return Response(status=status.HTTP_204_NO_CONTENT)
    
        def perform_destroy(self, instance):
            instance.delete()
    

    5、总结

      这一章简要分析了源码的内容以及各个mixins的逻辑,最重要的还是学会重写它们相关的方法。一般情况下,当我们在操作某一个model的时候,涉及到另外一个model中数据的修改,那么就需要对这个mixins下执行save的逻辑的方法进行重写。

    四、使用通用的基于类的视图

      通过使用mixin类,使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。

    from rest_framework import mixins, generics
    
    class AuthorView(generics.ListCreateAPIView):
        queryset = Author.objects.all()     # 配置queryset:告知这个类这次处理的数据
        serializer_class = AuthorModelSerializers     # 告知处理用到的序列化组件
    
    class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
        queryset = Author.objects.all()     # 配置queryset:告知这个类这次处理的数据
        serializer_class = AuthorModelSerializers     # 告知处理用到的序列化组件
    

    1、ListCreateAPIView源码

    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)
    

      可以看到源码中将Mixins混合类和getpost函数都封装进去了。

    2、RetrieveUpdateDestroyAPIView源码

    class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                       mixins.UpdateModelMixin,
                                       mixins.DestroyModelMixin,
                                       GenericAPIView):
        """
        Concrete view for retrieving, updating or deleting 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)
    
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

      其他RetrieveDestroyAPIView、RetrieveUpdateAPIView、UpdateAPIView、DestroyAPIView等封装方式完全类似。

    五、运用viewsets.ModelViewSet的视图

    1、改写urls.py

    from django.contrib import admin
    from django.urls import path, re_path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        '''代码省略'''
        # re_path(r'^authors/$', views.AuthorView.as_view(), name="author"),
        # re_path(r'^authors/(?P<pk>d+)/$', views.AuthorDetailView.as_view(), name="detail_author"),
    
        # as_view参数指定什么请求走什么方法
        re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),
        re_path(r'^authors/(?P<pk>d+)/$', views.AuthorViewSet.as_view({
            'get': 'retrieve',
            'put': 'update',
            'patch': 'partial_update',
            'delete': 'destroy'
        }), name="author_detail"),
    ]
    

      注意:

    (1)as_view参数

      利用参数来指定什么请求方式由哪一个内部方法来执行。尤其注意两种不同get请求用不同的方法来处理。

    2、引入viewsets改写视图

    from rest_framework import viewsets
    
    class AuthorViewSet(viewsets.ModelViewSet):
        queryset = Author.objects.all()     # 配置queryset:告知这个类这次处理的数据
        serializer_class = AuthorModelSerializers     # 告知处理用到的序列化组件
    

    五、ModelViewSet源码分析

    1、查看viewsets.ModelViewSet源码

    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
    

      ModelViewSet继承了所有五个混合类。还继承了GenericViewSet类。

    2、查看GenericViewSet源码

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        pass
    

      可以看到GenericViewSet继承了前面学的generics.GenericAPIView类,这个类扩展了REST框架的APIView类,但是它并没有改写dispatch方法,因此url中可以添加参数与它无关。

      GenericViewSet还继承了ViewSetMixin类。

    3、查看分析ViewSetMixin源码

    class ViewSetMixin(object):
        """
        Overrides `.as_view()` so that it takes an `actions` keyword that performs
        the binding of HTTP methods to actions on the Resource.
        覆盖as_view方法需要接收一个actions参数来实现将HTTP方法绑定到资源。
        For example, to create a concrete view binding the 'GET' and 'POST' methods
        to the 'list' and 'create' actions...
        举例来说,如果要创建一个具体视图绑定'GET'和'POST'方法到'list'和'create'操作。可写为如下格式:
        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.
            """
            # actions must not be empty   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'})`")
    
            # sanitize keyword arguments
            for key in initkwargs:.....   # 这里的值是空的
    
            def view(request, *args, **kwargs):...
    
            return csrf_exempt(view)    # 执行as_view 最终返回的还是view函数
    

      注意:

    (1)分析函数传参方式:**字典传参等同于关键字传参。

    def foo(action=None, **kwargs):
        print(action)
        print(kwargs)
    
    foo({"a": 1}, b=2, c=3)
    """
    {'a': 1}
    {'b': 2, 'c': 3}
    """
    foo(a=2, b=3)
    """
    None
    {'a': 2, 'b': 3}
    """
    foo(**{"a": 1, "b": 3})
    """
    None
    {'a': 1, 'b': 3}
    """
    foo({"a": 1, "b": 3})
    """
    {'a': 1, 'b': 3}
    {}
    """
    

      由此可见as_view传递的参数{"get": "list", "post": "create"}是ViewSetMixin内改写的as_view方法,由actions参数来接收的。

      由于acitons默认值是None,因此not None其实是True,if not actions: 其实是actions为空则报错的意思。

    (2)as_view函数最终返回值是view函数

    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  # 传入的字典数据{'get': 'list', 'post': 'create'}
    
        # Bind methods to actions
        # This is the bit that's different to a standard view
        for method, action in actions.items():  # method:请求方式   action:实例方法
            handler = getattr(self, action)  # 反射得到self.list  self.create方法
            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
        return self.dispatch(request, *args, **kwargs)
    

      在view函数中用self.action_map = actions 来接收传入的字典数据{'get': 'list', 'post': 'create'}。

      循环for method, action in actions.items():   拿到method:请求方式和action:实例方法。

      再通过反射方法:handler = getattr(self, action) 得到self.list self.create方法。

      最后通过反射方法setattr(self, method, handler)  给请求方式设置对应的实例方法:以后再找getattr(self, "get")的时候找到是self.list;找getattr(self, "post")的时候找到是self.create;

    (3)反射方法详解

    1. getattr()函数是Python自省的核心函数,具体使用大体如下:
    class A: 
    def __init__(self): 
    self.name = 'zhangjing'
    #self.age='24'
    def method(self): 
    print"method print"
      
    Instance = A() 
    print getattr(Instance , 'name, 'not find') #如果Instance 对象中有属性name则打印self.name的值,否则打印'not find'
    print getattr(Instance , 'age', 'not find') #如果Instance 对象中有属性age则打印self.age的值,否则打印'not find'
    print getattr(a, 'method', 'default') #如果有方法method,否则打印其地址,否则打印default 
    print getattr(a, 'method', 'default')() #如果有方法method,运行函数并打印None否则打印default 
    
    2. hasattr(object, name)
    
    说明:判断对象object是否包含名为name的特性(hasattr是通过调用getattr(ojbect, name)是否抛出异常来实现的)
    
    3. setattr(object, name, value)
    
    这是相对应的getattr()。参数是一个对象,一个字符串和一个任意值。字符串可能会列出一个现有的属性或一个新的属性。这个函数将值赋给属性的。该对象允许它提供。例如,setattr(x,“foobar”,123)相当于x.foobar = 123。
    
    4. delattr(object, name)
    
    与setattr()相关的一组函数。参数是由一个对象(记住python中一切皆是对象)和一个字符串组成的。string参数必须是对象属性名之一。该函数删除该obj的一个由string指定的属性。delattr(x, 'foobar')=del x.foobar
    

    4、找到view函数执行完后最终返回dispatch方法 并完成分析

      从ModelViewSet——》GenericViewSet——》GenericAPIView——》APIView一路回溯到APIView才找到dispatch方法。

    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    
        try:
            self.initial(request, *args, **kwargs)
            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            response = self.handle_exception(exc)
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    

      当访问是走的路由是re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),get请求handler对应的是self.list。

      而当访问走的路由是re_path(r'^authors/(?P<pk>d+)/$', views.AuthorViewSet.as_view({'get': 'retrieve',....

     此时get请求handler对应的是self.retrieve。

      如此就实现了两个类合成一个类。

    5、找到并执行list方法

      查看ModelViewSet源码:

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
    

      在ListModelMixin找到list方法:

    class ListModelMixin(object):
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            queryset = self.filter_queryset(self.get_queryset())
    
            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)
    
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
    

    六、视图组件总结

    1、DRF所有视图文件

      所有的视图类在如下这四个文件内:

    from rest_framework import views       # APIView
    from rest_framework import generics    # 公共通用视图类:GenericAPIView,及各种组合视图类CreateAPIView、ListAPIView、RetrieveAPIView等
    from rest_framework import mixins      # 混合继承类:CreateModelMixin、ListModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin
    from rest_framework import viewsets    # 重写as_view: ViewSetMixin;其他类都是帮助去继承ViewSetMixin

    2、DRF视图图谱

      视图类继承顺序,如Django REST Framework视图图谱所示:

      

      首先 django是继承 view的,DRF是从APIView开始继承起,APIView封装了request,其中包含了data、query_params等属性、方法。

      然后 GenericAPIView封装了 get_queryset() 和 get_serializer();ViewSetMixin重写了 as_view()方法。

      随后 GenericViewSet帮忙继承GenericAPIView和ViewSetMixin。

      最后最高层的封装是 ModelViewSet。

      

  • 相关阅读:
    Spring框架的反序列化远程代码执行漏洞分析(转)
    JVM调优总结(转)
    创造与熟练与微创造
    一次 react-router 中遇到的小坑
    mac中强大的快捷键
    数组思维 -- join的一些用法感悟
    undefined null 与 字符串相加
    mobx中的数组需要注意的地方
    更换鼠标垫(鼠标)的心路历程
    Object.defineProperty 中的 writable 和 configurable 和 enumerable 的理解
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9576582.html
Copyright © 2020-2023  润新知