DRF之视图组

  • 先大概看一下这几个 视图类 的关系

    视图组.jpg

  • 使用APIView,定义方法

    views中:
    from rest_framework.views import APIView
    
    class BookView(APIView):
        # 查看所有
        def get(self, request):
            pass
        # 添加单条
        def post(self, request):
           pass
    class SBookView(APIView):
        # 查看单条
        def get(self, request, id):
           pass
        # 更改单条
        def put(self, request, id):
            pass
        # 删除单条
        def delete(self, request, id):
            pass
    
    对应的url:
       url(r'^book/$', views.BookView.as_view()),  # 必须加上结尾符,否则匹配不到下面
       url(r'^book/(\d+)/', views.SBookView.as_view()),
    
  • 进一步封装

    views1中:
    from rest_framework import generics
    # 处理增删改查查的五个类,每个类里都有各自的处理方法
    from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
      RetrieveModelMixin
    
    from app01.models import Publish
    
    class PublishSerializer(serializers.ModelSerializer):
        class Meta:
            model = Publish
            fields = '__all__'
    
    
    class PublishView(ListModelMixin, CreateModelMixin, generics.GenericAPIView):  # 【APIView由这个类里继承】
        queryset = Publish.objects.all()  # 【这两个变量必须交这个名字,因为继承类里面要拿这个变量去处理】
        serializer_class = PublishSerializer
    
        def get(self, request, *args, **kwargs):
            return self.list(request, *args, **kwargs)  # 在ListModelMixin里有这个方法
    
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)  # 在CreateModelMixin里有这个方法
      
    
    class SPublishView(UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin, generics.GenericAPIView):
        queryset = Publish.objects.all()
        serializer_class = PublishSerializer
    
        def get(self,request,*args,**kwargs):
            return self.retrieve(request,*args,**kwargs)  # 在 RetrieveModelMixin里有这个方法
    
        def put(self,request,*args,**kwargs):
            return self.update(request,*args,**kwargs)  # 在 UpdateModelMixin里有这个方法
    
        def delete(self,request,*args,**kwargs):
            return self.destroy(request,*args,**kwargs)  # 在 DestroyModelMixin里有这个方法
    
    对应的url:
        url(r'^publishes/$',views1.PublishView.as_view()),
        url(r'^publishes/(?P<pk>\d+)/', views1.SPublishView.as_view()),  # 参数必须以此命名,因为函数处理时就接收这个参数
    
  • 再一步封装

    views2中:
    from rest_framework import generics
    from app01.models import Author
    
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = '__all__'
    
    # 简直太省了,把get和post等方法都封装进了类里。
    class AuthorsView(generics.ListCreateAPIView):  # 这个类里提供get,post两个方法
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
    
    class SAuthorsView(generics.RetrieveUpdateDestroyAPIView):  # 这个类提供了get/1,put/1,delete/1。3个方法
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    # 上面两个类对之前的类又进行了封装
    
    对应的url:
        url(r'^authors/$',views2.AuthorsView.as_view()),
        url(r'^authors/(?P<pk>\d+)/', views2.SAuthorsView.as_view()),
    
  • 终极封装

    views3中:
    from rest_framework.viewsets import ModelViewSet
    
    from app01.models import Author
    from app01.serializer import AuthorSerializer
    
    class AuthorsView(ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorSerializer
    
    对应的url:
        url(r'^authorsss/$',views3.AuthorsView.as_view({'get':'list','post':'create'})),
        url(r'^authorsss/(?P<pk>\d+)/',views3.AuthorsView.as_view({'get':'retrieve','delete':'destroy','put':'update'}))
        # ↑上面的as_view中的参数必须以此命名
    

封装的越深,约束性越大,功能的扩展性就越差,所以最后可根据不同的业务逻辑去使用不同的方法。

  • ModelViewSet源码流程
    • 1,继承ModelViewSet

      class AuthorsView(ModelViewSet):
          queryset = Author.objects.all()
          serializer_class = AuthorSerializer
      
    • 2,先进去,看看里面的结构,继承了哪些类

      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
      
    • 3,前5个类,分别封装了几种不同的操作方法,最后一个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
      
    • 4,左边的ViewSetMixin,有as_view方法,将方法赋给其对象

      class ViewSetMixin(object):
      # 如果使用ModelViewSet,则执行的as_view就是ViewSetMixin中的as_view方法。
          @classonlymethod
          def as_view(cls, actions=None, **initkwargs):
              # 如果没有定义,就报错,所以必须那样写。
              if not actions:
                  raise TypeError("The `actions` argument must be provided when "
                                  "calling `.as_view()` on a ViewSet. For example "
                                  "`.as_view({'get': 'list'})`")
      
              def view(request, *args, **kwargs): 
                  self = cls(**initkwargs)
                  self.action_map = actions
      
                  for method, action in actions.items(): # 核心代码:把参数里面的键值对,以self.method = ...的形式,变成了self的属性
                      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
      
                  return self.dispatch(request, *args, **kwargs)
      
              return csrf_exempt(view)
      
    • 5,右边的generics.GenericAPIView继承了APIView

      class GenericAPIView(views.APIView):
      
    • 6,APIView中有dispatch方法

      def dispatch(self, request, *args, **kwargs):
          self.args = args
          self.kwargs = kwargs  # 路由中传的命名参数 
          # 1,重新封装request对象
          request = self.initialize_request(request, *args, **kwargs)
          self.request = request
          self.headers = self.default_response_headers  # deprecate?
      
          try: 
              # 2,认证,权限,频率
              self.initial(request, *args, **kwargs)
              
              # 【用字符串,反射self里面对应的方法,此步之后就有了self.get/self.post】
              # 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
      
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容