zoukankan      html  css  js  c++  java
  • drf 视图源码详解

    mixin类和Generic类

    导入

    from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, UpdateModelMixin, DestroyModelMixin
    from rest_framework.generics import GenericAPIView
    

    CreateModelMixin 创建

    源码路径为 rest_framework —> mixing.py —>CreateModelMixin

    class CreateModelMixin(object):
        """
        Create a model instance.
        """
        def create(self, request, *args, **kwargs):
            # 传入前端传入的数据,进行反序列化
            serializer = self.get_serializer(data=request.data)
            
            # 判断是否全部都校验通过 通过了就执行下面语句
            # raise_exception=True 不通过直接报错,下面也不会执行
            serializer.is_valid(raise_exception=True)
            
            # 执行perform_create 实际就是.save() 保存
            self.perform_create(serializer)
            
            # 头部信息忽略
            headers = self.get_success_headers(serializer.data)
            
            # 遵循规范返回创建对象的数据
            # status 状态信息,对应的是一个个状态码
            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 {}
    

    视图使用-post

    class PublishView(CreateModelMixin,GenericAPIView):
      	queryset = models.Publish.objects.all()
        
      	# 内部执行的时候CreateModelMixin调用了serializer_class
        serializer_class = PublishSerializers
        
      		# 执行post请求就说明要添加数据
        def post(self, request, *args, **kwargs):
          # 执行create 自己没有就执行CreateModelMixin 
          # 也就是上面的CreateModelMixin源码解析
            return self.create(request, *args, **kwargs)
    

    ListModelMixin - 查看多条数据

    源码路径为 rest_framework —> mixing.py —>ListModelMixin

    class ListModelMixin(object):
        """
        List a queryset.
        """
        def list(self, request, *args, **kwargs):
            # 过滤相关
            # 这个时候调用了GenericAPIView内的get_queryset
            queryset = self.filter_queryset(self.get_queryset())
            
            # 分页相关
            page = self.paginate_queryset(queryset)
    
            # page不为空的情况下
            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)
    

    视图-get-取所有数据

    class PublishView(ListModelMixin,GenericAPIView):
      	queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
        
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    

    RetrieveModelMixin 获取单条数据

    源码路径为 rest_framework —> mixing.py—>RetrieveModelMixin

    class RetrieveModelMixin(object):
    
        def retrieve(self, request, *args, **kwargs):
            # 获取单条
            instance = self.get_object()
            # 获取到序列化返回
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    

    视图-get-取指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
    
        # 获取指定一条
        def get(self, request, *args, **kwargs):
            return self.retrieve(request, *args, **kwargs)
    

    UpdateModelMixin 更新单条数据

    源码路径为 rest_framework —> mixing.py—>UpdateModelMixin

    class UpdateModelMixin(object):
        """
        Update a model instance.
        """
        def update(self, request, *args, **kwargs):
    
            partial = kwargs.pop('partial', False)  
    
            # 要序列化单条数据
            instance = self.get_object()
            # 序列化
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            # 校验是否全部通过
            serializer.is_valid(raise_exception=True)
            # 直接保存
            self.perform_update(serializer)
    
            if getattr(instance, '_prefetched_objects_cache', None):
                instance._prefetched_objects_cache = {}
    
            return Response(serializer.data)
    
        def perform_update(self, serializer):
            serializer.save()
    
        def partial_update(self, request, *args, **kwargs):
            kwargs['partial'] = True
            return self.update(request, *args, **kwargs)
    

    视图 - put- 更新指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
    
        # 更新指定数据
        def put(self, request, *args, **kwargs):
            return self.update(request, *args, **kwargs)
    

    DestroyModelMixin - 删除指定数据

    class DestroyModelMixin(object):
        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()
    

    视图 - delete- 删除指定数据

    class PublishDetailView(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
        queryset = models.Publish.objects.all()
        serializer_class = PublishSerializers
        
        # 删除指定数据
        def delete(self, request, *args, **kwargs):
            return self.destroy(request, *args, **kwargs)
    

    GenericAPIView 通用的apiview

    get_queryset(self):

    def get_queryset(self):
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )
        # 这个时候的queryset 是传递进来的queryset对象
        queryset = self.queryset
        # 判断是否是QuerySet,不是QuerySet 自动.all()
        # 说明queryset可以直接给它传递一个对象如 models.Publish.objects
        if isinstance(queryset, QuerySet):
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all()
            # 返回queryset对象
        return queryset
    

    generics 里面组合了mixins里面方法和GenericAPIView

    导入

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    

    url

    url(r'^publish/$', views.publishView.as_view()),
    url(r'^publish/(?P<pk>d+)/$', views.PublishDetailView.as_view())
    

    ListCreateAPIView源码 创建与查询多条

    class ListCreateAPIView(mixins.ListModelMixin,
                            mixins.CreateModelMixin,
                            GenericAPIView):
      	# 获取多条
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)
    		# 创建数据
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    

    RetrieveUpdateDestroyAPIView源码 查询单条 更新 删除

    class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
                                       mixins.UpdateModelMixin,
                                       mixins.DestroyModelMixin,
                                       GenericAPIView):
    		# 获取指定数据
        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)
    

    视图

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    
    class publishView(ListCreateAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    
    class PublishDetailView(RetrieveUpdateDestroyAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    

    ModelViewSet 多种请求映射

    导入:

    from rest_framework.viewsets import ModelViewSet
    
    • 路由:
        url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
        url(r'^publish/(?P<pk>d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),
    
    • 视图:
    from rest_framework.viewsets import ModelViewSet
    
    class PublishView(ModelViewSet):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
        
    # 这里面虽然可以满足方法的请求但是有时候我们会更具业务的需要,去修改,我们可以重写指定的方法,如create等
    

    为什么可以可以做到请求映射?

    • 主要是重写了as_view方法

    1.ModelViewSet中唯一不通的就是继承了GenericViewSet,上面的方法中继承的是GenericAPIView 俩者不同

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

    2.GenericViewSet中pass??,继承了之前的GenericAPIView,但是多继承了一个ViewSetMixin

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        pass
    

    3.ViewSetMixin中仔细看as_view 和原生的Django.views.View.as_view的区别,多了一个参数

    Django.views.View.as_view
    def as_view(cls, **initkwargs):
      	pass
      
    ViewSetMixin.as_view  
    def as_view(cls, actions=None, **initkwargs):
      	pass
     
    

    4.所以路由中配置的字典{'get': 'list', 'post': 'create'}给了actions

     url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})),
     url(r'^publish/(?P<pk>d+)/$',
            views.PublishView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
    

    5.ViewSetMixin.as_view 源码中如果可以看出如果继承了ViewSetMixin没有传递actions直接就会异常

    if not actions:
        raise TypeError("The `actions` argument must be provided when "
                        "calling `.as_view()` on a ViewSet. For example "
                        "`.as_view({'get': 'list'})`")
    

    6.ViewSetMixin.as_view核心代码,也就是这一段代码让我们的请求直接执行对应的执行函数

    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        # 调用了传入的actions 赋值给了action_map
        self.action_map = actions
        
        # {'get': 'list', 'post': 'create'}
        # 解压赋值 请求给了method 处理参数给了action
        for method, action in actions.items():
            # self是自定义的那个路由函数的对象
            # 通过反射去对象中找action
            # handler就相当于 那个执行方法的内存地址
            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
        return self.dispatch(request, *args, **kwargs)
    

    自定义ViewSetMixin类

    • 相当于映射 指定请求来了执行指定方法
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.views import APIView
    
    # 要注意继承的顺序如果APIView继承在前按照mro列表先执行它里面的as_views,所以要放在前面
    class Test(ViewSetMixin, APIView):
        def test(self, request, pk):
            print('自定义请求收到参数:', pk)
            return Response()
    

    url

    url(r'^test/(?P<pk>d+)/$', views.Test.as_view({'get': 'test'})),
    
  • 相关阅读:
    ACM ICPC 2008–2009 NEERC MSC A, B, C, G, L
    POJ 1088 滑雪 DP
    UVA 11584 最短回文串划分 DP
    POJ 2531 Network Saboteur DFS+剪枝
    UVa 10739 String to Palindrome 字符串dp
    UVa 11151 Longest Palindrome 字符串dp
    UVa 10154 Weights and Measures dp 降维
    UVa 10271 Chopsticks dp
    UVa 10617 Again Palindrome 字符串dp
    UVa 10651 Pebble Solitaire 状态压缩 dp
  • 原文地址:https://www.cnblogs.com/limengda/p/11133779.html
Copyright © 2011-2022 走看看