DRF进阶

命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

导读:本篇文章讲解 DRF进阶,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

DRF框架请求与响应

  • DRF 之Request

    Request是包装后的request,前面源码分析过了

    • 新包装的Request对象和原来django的request对象没有什么区别,使用方法一样,只是多了request.data,当然区别也不止这一点,我们看一下目录

    image

    • request常用方法

      • 新包装的Request对象是通过__getattr__方法反射过来的
        def __getattr__(self, attr):
            try:
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)
      • 现在数据都可以通过request.data获取
        @property
        def data(self):
            if not _hasattr(self, '_full_data'):
                self._load_data_and_files()
            return self._full_data
      • 原来request请求提交的数据在GET里,现在请求参数可以通过request.query_parmas来查询参数,也是被伪装成数据属性了,本质还是使用了原来request对象的GET,所以请求参数既可以从GET中取,也可以从request.query_params中取
        @property
        def query_params(self):
            """
            More semantically correct name for request.GET.
            """
            return self._request.GET
    • 配置请求数据格式

      默认的情况下,三种数据格式(urlencoded,formdata,json)都可以解析
      比如当请求(post)过来要新增一条数据,那么我们可以针对该请求的数据格式做要求,比如只能提交json格式,或者允许From-data和json等···通过写接口实现,需要进行局部(views)或者全局配置(settings)

      • 配置处理顺序
        • 局部优先
        • 全局其次
        • 默认配置最后
      • 局部配置
        • 导入:from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
        • 配置参数:parser_classes

          配置在视图类里

          '''views.py'''
          # 作者详情视图类
          from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
          class AuthorDetailView(APIView):
          
          # 只处理json格式,默认三种格式都可以
              # parser_classes = [JSONParser, FormParser, MultiPartParser]
              parser_classes = [JSONParser]
              def post(self, request):
                  # 获取反序列化数据
                  ser = serializer.AuthorDetailSerializer(data=request.data)
                  if ser.is_valid():
                      # 校验通过存入数据库,不需要重写create方法了
                      ser.save()
                      return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
                  # 校验失败
                  return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})

          imageimageimage

          在局部配置的时候千万注意,如果parser_classes=[],那么什么数据格式都不解析

      • 全局配置
        • 导入:from rest_framework import settings
        • 源码解释
          '''Settings for REST framework are all namespaced in the REST_FRAMEWORK setting.
          For example your project's `settings.py` file might look like this:
          
          REST_FRAMEWORK = {
              'DEFAULT_RENDERER_CLASSES': [
                  'rest_framework.renderers.JSONRenderer',
                  'rest_framework.renderers.TemplateHTMLRenderer',
              ],
              'DEFAULT_PARSER_CLASSES': [
                  'rest_framework.parsers.JSONParser',
                  'rest_framework.parsers.FormParser',
                  'rest_framework.parsers.MultiPartParser',
              ],
          }
          '''
        • 全局settings.py配置
          from rest_framework import settings
          
          REST_FRAMEWORK = {
          'DEFAULT_PARSER_CLASSES': [
                  'rest_framework.parsers.JSONParser',
                  'rest_framework.parsers.FormParser',
                  'rest_framework.parsers.MultiPartParser',
              ],
          }
          
          '''默认是三种数据格式都解析的,想解析哪种写哪种就行了'''

          一般使用默认配置就可以了,如果需要搭配配置,局部和全局也可以一起使用

  • DRf 之 Response

    导入:from rest_framework.response import Response

    • Reponse常用参数

      源码

      class Response(SimpleTemplateResponse):
          def __init__(self, data=None, status=None,
                       template_name=None, headers=None,
                       exception=False, content_type=None):
              ·····
      • data:一般处理成字符串,字典,列表等,给http响应body体中的内容
        注意,data可以从response对象中取出来二次处理,返回成我想要的样子
        Response.data['msg'] = '再加点料'
      • status:响应状态码,可以导入,比如:from rest_framework.status import HTTP_201_CREATED,但是建议自定义,不写默认是200
      • headers:响应头(字典)
      • template_name:模板名称,用浏览器访问需要,默认使用DRF提供的,可以使用自己的
      • exception:异常处理
      • content_type:响应编码格式
    • 局部配置

      导入:from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer

      • JSONRenderer:json格式渲染器
      • BrowsableAPIRenderer:浏览API渲染器

      通过renderer_classes配置解析格式

      '''views.py'''
      from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
      
      class PublishView(APIView):
          renderer_classes = [JSONRenderer, ]
      
          ····

      image

      ps: 如果只配置JSONRenderer,那么浏览器访问也是json格式数据

    • 全局配置

      settings.py配置

      默认两种都解析,取各自所需

      REST_FRAMEWORK = {
          'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
              'rest_framework.renderers.JSONRenderer',  # json渲染器
              'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
          )
      }

视图组件

  • 视图组件大纲

    • 两个视图基本类

      导入:

      • from rest_framework.views import APIView
      • from rest_framework.generics import GenericAPIView
      • APIView:DRF最顶层视图类
      • GenericAPIView:DRF通用视图类
    • 五个扩展类

      扩展类不是视图类,没有继承APIView,需要配合GenericAPIView使用,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法
      主要是用来对数据进行增删改查

      导入

      • from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
      • CreateModelMixin
      • ListModelMixin
      • DestroyModelMixin
      • RetrieveModelMixin
      • UpdateModelMixin
    • 九个子类视图

      导入

      from rest_framework.generics import CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView

      视图子类其实可以理解为GenericAPIView通用视图类和Mixin扩展类的排列组合组成的,底层事通过封装和继承来写

      • CreateAPIView
        提供 post 方法
        继承自: GenericAPIView、CreateModelMixin
      • ListAPIView
        提供 get 方法
        继承自:GenericAPIView、ListModelMixin
      • DestroyAPIView
        提供 delete 方法
        继承自:GenericAPIView、DestoryModelMixin
      • RetrieveAPIView
        提供 get 方法
        继承自: GenericAPIView、RetrieveModelMixin
      • UpdateAPIView
        提供 put 和 patch 方法
        继承自:GenericAPIView、UpdateModelMixin
      • ListCreateAPIView
        提供get 和 post方法
        继承自:ListModelMixin、CreateModelMixin、GenericAPIView
      • RetrieveUpdateAPIView
        提供 get、put、patch方法
        继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
      • RetrieveDestroyAPIView
        提供:get、delete方法
        继承自:RetrieveModelMixin、DestroyModelMixin、GenericAPIView
      • RetrieveUpdateDestroyAPIView
        提供 get、put、patch、delete方法
        继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
    • 视图集

      导入

      from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin

      • 常用视图集父类
        • ModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
        • ReadOnlyModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
        • ViewSet:继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典({‘get’:’list’})的映射处理工作。
          • 在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
            GenericViewSet:使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
        • GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{‘get’:’list’}`)的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
      • 魔法类
        • ViewSetMixin:控制自动生成路由
    • 一览表

      image

  • 视图组件演变

    • 基于APIview的五个接口

      class BookView(APIView):
          def get(self, requets):
              # 序列化
              book_list = models.Book.objects.all()
              # 序列化多条数据many=True
              ser = serializer.BookSerializer(instance=book_list, many=True)
              return Response(ser.data)
      
          def post(self, request):
              # 获取反序列化数据
              ser = serializer.BookSerializer(data=request.data)
              if ser.is_valid():
                  # 校验通过存入数据库,不需要重写create方法了
                  ser.save()
                  return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
              # 校验失败
              return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
      
      
      class BookViewDetail(APIView):
          def get(self, request, pk):
              book = models.Book.objects.filter(pk=pk).first()
              ser = serializer.BookSerializer(instance=book)
              return Response(ser.data)
      
          def put(self, request, pk):
              book = models.Book.objects.filter(pk=pk).first()
              # 修改,instance和data都要传
              ser = serializer.BookSerializer(instance=book, data=request.data)
              if ser.is_valid():
                  # 校验通过修改,不需要重写update
                  ser.save()
                  return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
              # 校验不通过
              return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
      
          def delete(self, request, pk):
              models.Book.objects.filter(pk=pk).delete()
              return Response({'code': 100, 'msg': '删除成功'})
    • 基于GenericAPIView的五个接口

      • 常见属性
        -GenericAPIView   继承了APIView,封装了一些属性和方法,跟数据库打交道
            -queryset = None # 指定序列化集
            -serializer_class = None  # 指定序列化类
            -lookup_field = 'pk'  # 查询单条,分组分出来的参数,转换器对象参数的名字
            -filter_backends   # 过滤排序功能会用它
            -pagination_class  # 分页功能
            
            -get_queryset()  # 获取要序列化的数据,后期可能会重写
            -get_object()    # 通过lookup_field查询的
            -get_serializer()  # 使用它序列化
            -get_serializer_class() # 返回序列化类 ,后期可能重写
            
            
            
        demo:
        # 指定序列化集
        queryset = models.Book.objects.all()
        # 指定序列化类
        serializer_class = serializer.BookSerializer
      • 五个接口demo
        from rest_framework.response import Response
        
        
        from app01 import models
        from app01 import serializer
        from rest_framework.generics import GenericAPIView
        # 书视图类
        class BookView(GenericAPIView):
            # 指定序列化集
            queryset = models.Book.objects.all()
            # 指定序列化类
            serializer_class = serializer.BookSerializer
            def get(self, requets):
                # obj = self.queryset()
                obj = self.get_queryset() # 等同于上面
                # ser = self.get_serializer_class()(instance=obj,many=True)
                ser = self.get_serializer(instance=obj,many=True) # 等同于上面
                return Response(ser.data)
        
            def post(self, request):
                # 获取反序列化数据
                # ser = serializer.BookSerializer(data=request.data)
                ser = self.get_serializer(data = request.data)
                if ser.is_valid():
                    # 校验通过存入数据库,不需要重写create方法了
                    ser.save()
                    return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
                # 校验失败
                return Response({'code': 101, 'msg': '校验未通过', 'error': ser.errors})
        
        
        class BookViewDetail(GenericAPIView):
            # 指定序列化集
            queryset = models.Book.objects.all()
            # 指定序列化类
            serializer_class = serializer.BookSerializer
            def get(self, request, pk):
                # book = models.Book.objects.filter(pk=pk).first()
                book = self.get_object() # 根据pk拿到单个对象
                # ser = serializer.BookSerializer(instance=book)
                ser = self.get_serializer(instance=book)
                return Response(ser.data)
        
            def put(self, request, pk):
                # book = models.Book.objects.filter(pk=pk).first()
                book = self.get_object()
                # 修改,instance和data都要传
                # ser = serializer.BookSerializer(instance=book, data=request.data)
                ser = self.get_serializer(instance=book,data=request.data)
                if ser.is_valid():
                    # 校验通过修改,不需要重写update
                    ser.save()
                    return Response({'code:': 100, 'msg': '修改成功', 'data': ser.data})
                # 校验不通过
                return Response({'code:': 102, 'msg': '校验未通过,修改失败', 'error': ser.errors})
        
            def delete(self, request, pk):
                # models.Book.objects.filter(pk=pk).delete()
                self.get_object().delete()
                return Response({'code': 100, 'msg': '删除成功'})
      • 路由
        path('books/', views.BookView.as_view()),
        path('books/<int:pk>', views.BookViewDetail.as_view())
      • 总结:到第二层只需修改queryset和serializer_class类属性即可,其余都不需要修改
      • 注意:虽然pk没有在orm语句中过滤使用,但是路由分组要用,所以不能删,或者写成*args **kwargs接收多余的参数,且路由转换器必须写成pk
        # 源码
        lookup_field = 'pk'
        lookup_url_kwarg = None
        
        # get_queryset()方法可以重写,如果我们需要在一个视图类内操作另外表模型,来指定序列化的数据
        class BookViewDetail(GenericAPIView):
            queryset = models.Book.objects.all()
            ···
            '''
            指定序列化数据的格式:
            self.queryset()
            self.get_queryset() # 等同于上面
            queryset = models.Book.objects.all()
            '''
            # 可以重写get_queryset方法在book视图类里操作作者模型
            def get_queryset(self,request):
                if self.request.path == '/user'
                return Author.objects.all()
            ···
            # 这样序列化的数据就不一样了,根据不同的条件序列化不同的数据
            
            '''当然还可以通过重写get_serializer_class来返回其他序列化器类'''
    • 基于GenericAPIView+五个视图扩展类写

      五个视图扩展类:

      • from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin

      通过GenericAPIView+视图扩展类来使得代码更简单,一个接口对应一个扩展类,注意扩展类不是视图类

      • ListModelMixin:获取所有API,对应list()方法
      • CreateModelMixin:新增一条API,对应create()方法
      • UpdateModelMixin:修改一条API,对应update()方法
      • RetrieveModelMixin:获取一条API,对应retrieve()方法
      • DestroyModelMixin:删除一条API,对应destroy()方法
      • 注意:CreateModelMixin扩展类提供了更高级的方法,可以通过重写来校验数据存入
        def perform_create(self, serializer):
            serializer.save()
      • 五个接口的demo
        from app01 import models
        from app01 import serializer
        from rest_framework.generics import GenericAPIView
        from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
        
        # 获取所有和新增API
        class BookView(ListModelMixin,CreateModelMixin,GenericAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
            def get(self, request):
               return super().list(request)
        
            def post(self, request):
                return super().create(request)
        
        # 获取删除修改单个API
        class BookViewDetail(UpdateModelMixin,RetrieveModelMixin,DestroyModelMixin,GenericAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
            def get(self, request, *args,**kwargs):
                return super().retrieve(request, *args,**kwargs)
        
            def put(self, request, *args,**kwargs):
                return super().update(request, *args,**kwargs)
        
            def delete(self, request, *args,**kwargs):
                return super().destroy(request, *args,**kwargs)
      • GenericAPIView速写五个接口demo
        • 模型
          from django.db import models
          
          
          # Create your models here.
          
          # build four model tables
          
          class Book(models.Model):
              name = models.CharField(max_length=32)
              price = models.DecimalField(decimal_places=2, max_digits=5)
              publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
              authors = models.ManyToManyField(to='Author')
              def __str__(self):
                  return self.name
          
              # 自定制字段
              @property
              def publish_detail(self):
                  return {'name': self.publish.name, 'addr': self.publish.city}
          
          
              @property
              def author_list(self):
                  l = []
                  print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
          
                  for author in self.authors.all():
                      print(author.author_detail) # AuthorDetail object (1)
                      l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr})
                  return l
          
          
          class Author(models.Model):
              name = models.CharField(max_length=32)
              age = models.IntegerField()
              author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
          
              def __str__(self):
                  return self.name
          
              @property
              def authordetail_info(self):
                  return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr}
          
          
          class AuthorDetail(models.Model):
              telephone = models.BigIntegerField()
              addr = models.CharField(max_length=64)
          
          
          class Publish(models.Model):
              name = models.CharField(max_length=32)
              city = models.CharField(max_length=32)
              email = models.EmailField()
        • 序列化器
          from django.db import models
          
          
          # Create your models here.
          
          # build four model tables
          
          class Book(models.Model):
              name = models.CharField(max_length=32)
              price = models.DecimalField(decimal_places=2, max_digits=5)
              publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
              authors = models.ManyToManyField(to='Author')
              def __str__(self):
                  return self.name
          
              # 自定制字段
              @property
              def publish_detail(self):
                  return {'name': self.publish.name, 'addr': self.publish.city}
          
          
              @property
              def author_list(self):
                  l = []
                  print(self.authors.all()) # <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
          
                  for author in self.authors.all():
                      print(author.author_detail) # AuthorDetail object (1)
                      l.append({'name': author.name, 'age': author.age, 'addr': author.author_detail.addr})
                  return l
          
          
          class Author(models.Model):
              name = models.CharField(max_length=32)
              age = models.IntegerField()
              author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
          
              def __str__(self):
                  return self.name
          
              @property
              def authordetail_info(self):
                  return {'phone':self.author_detail.telephone,'addr':self.author_detail.addr}
          
          
          class AuthorDetail(models.Model):
              telephone = models.BigIntegerField()
              addr = models.CharField(max_length=64)
          
          
          class Publish(models.Model):
              name = models.CharField(max_length=32)
              city = models.CharField(max_length=32)
              email = models.EmailField()
        • 视图
          from rest_framework.generics import GenericAPIView
          from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, \
              UpdateModelMixin
          
          from app01 import models
          from app01 import serializer
          
          
          # 书视图类
          class BookView(ListModelMixin, CreateModelMixin, GenericAPIView):
              queryset = models.Book.objects.all()
              serializer_class = serializer.BookSerializer
          
              def get(self, request):
                  return super().list(request)
          
              def post(self, request):
                  return super().create(request)
          
          
          class BookViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
              queryset = models.Book.objects.all()
              serializer_class = serializer.BookSerializer
          
              def get(self, request, *args, **kwargs):
                  return super().retrieve(request, *args, **kwargs)
          
              def put(self, request, *args, **kwargs):
                  return super().update(request, *args, **kwargs)
          
              def delete(self, request, *args, **kwargs):
                  return super().destroy(request, *args, **kwargs)
          
          
          # 作者
          class AuthorView(ListModelMixin, CreateModelMixin, GenericAPIView):
              queryset = models.Author.objects.all()
              serializer_class = serializer.AuthorSerializer
          
              def get(self, request):
                  return super().list(request)
          
              def post(self, request):
                  return super().create(request)
          
          
          class AuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
              queryset = models.Author.objects.all()
              serializer_class = serializer.AuthorSerializer
          
              def get(self, request, *args, **kwargs):
                  return super().retrieve(request, *args, **kwargs)
          
              def put(self, request, *args, **kwargs):
                  return super().update(request, *args, **kwargs)
          
              def delete(self, request, *args, **kwargs):
                  return super().destroy(request, *args, **kwargs)
          
          
          # 作者详情
          
          class AuthorDetailView(ListModelMixin, CreateModelMixin, GenericAPIView):
              queryset = models.AuthorDetail.objects.all()
              serializer_class = serializer.AuthorDetailSerializer
          
              def get(self, request):
                  return super().list(request)
          
              def post(self, request):
                  return super().create(request)
          
          
          class OneAuthorViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
              queryset = models.AuthorDetail.objects.all()
              serializer_class = serializer.AuthorDetailSerializer
          
              def get(self, request, *args, **kwargs):
                  return super().retrieve(request, *args, **kwargs)
          
              def put(self, request, *args, **kwargs):
                  return super().update(request, *args, **kwargs)
          
              def delete(self, request, *args, **kwargs):
                  return super().destroy(request, *args, **kwargs)
          
          
          # 出版社
          class PublishView(ListModelMixin, CreateModelMixin, GenericAPIView):
              queryset = models.Publish.objects.all()
              serializer_class = serializer.PublishSerializer
          
              def get(self, request):
                  return super().list(request)
          
              def post(self, request):
                  return super().create(request)
          
          
          class PublishViewDetail(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView):
              queryset = models.Publish.objects.all()
              serializer_class = serializer.PublishSerializer
          
              def get(self, request, *args, **kwargs):
                  return super().retrieve(request, *args, **kwargs)
          
              def put(self, request, *args, **kwargs):
                  return super().update(request, *args, **kwargs)
          
              def delete(self, request, *args, **kwargs):
                  return super().destroy(request, *args, **kwargs)
        • 路由
          from django.contrib import admin
          from django.urls import path
          
          from app01 import views
          
          urlpatterns = [
              path('admin/', admin.site.urls),
              # 书
              path('books/', views.BookView.as_view()),
              path('books/<int:pk>', views.BookViewDetail.as_view()),
          
              # 作者
              path('authors/', views.AuthorView.as_view()),
              path('authors/<int:pk>', views.AuthorViewDetail.as_view()),
          
              # 作者详情
              path('authorsdetail/', views.AuthorDetailView.as_view()),
              path('authorsdetail/<int:pk>', views.OneAuthorViewDetail.as_view()),
          
              # 出版社
              path('publish/', views.PublishView.as_view()),
              path('publish/<int:pk>', views.PublishViewDetail.as_view()),
          ]
    • 基于GenericAPIView+九个视图子类写五个接口

      使用哪个继承哪个就可以了,具体可以看继承的父类里有什么方法不需要刻意去记

      from rest_framework.generics import  CreateAPIView,ListAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView,ListCreateAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView,RetrieveDestroyAPIView
      
      # 1、查询所有,新增API
      class BookView(ListCreateAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 2、新增接口
      class BookView(CreateAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 3、查询接口
      class BookView(ListAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 4、查询单个,修改一个,删除一个接口
      class BookViewDetail(RetrieveUpdateDestroyAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 5、查询单个接口
      class BookViewDetail(RetrieveAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 6、修改单个接口
      class BookViewDetail(UpdateAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 7、删除单个接口
      class BookViewDetail(DestroyAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      # 8、查询单个、修改接口
      class BookViewDetail(RetrieveUpdateAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      
      # 9、查询单个、删除接口
      class BookViewDetail(RetrieveDestroyAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      
      '''上述共九个视图子类,九九归一剑诀~'''
      
      # 更新和删除接口自己整合
      class BookViewDetail(UpdateAPIView,DestroyAPIView):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      • 五接口速写
        from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
        
        from app01 import models
        from app01 import serializer
        
        class BookView(ListCreateAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
        
        class BookViewDetail(RetrieveUpdateDestroyAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
        '''其余的和第三层一样'''
    • 基于ViewSet写五个接口

      • 路由
        • 导入:from rest_framework.routers import SimpleRouter,DefaultRouter

        • 方法一:

          from django.urls import path, include
          from rest_framework.routers import SimpleRouter
          from app01 import views
          
          router = SimpleRouter()
          router.register('books', views.BookView, 'books')
          urlpatterns = [
              ...
          ]
          urlpatterns += router.urls
          '''
          register(self, prefix, viewset, basename=None)
          prefix:路由url前缀
          viewset:处理请求的viewset类
          basename:路由名称的前缀,一般和prefix写成一样就行
          '''
          # 等同于
          path('books/'),include(router.urls)
          path('books/<int:pk>'),include(router.urls)
        • 方法二:

          router = SimpleRouter()
          router.register('books', views.BookView, 'books')
          
          urlpatterns = [
              ...
              url(r'^', include(router.urls))
          ]
          
          
          # 生成两种路由
          path('/api/v1'),include(router.urls)
          # [<URLPattern '^books/$' [name='books-list']>, <URLPattern '^books/(?P<pk>[^/.]+)/$' [name='books-detail']>]
          # 等同于自己配的
          path('/api/v1/books/'),include(router.urls)
          path('/api/v1/books/<int:pk>'),include(router.urls)
        • 异同:

          同:方法一和方法二都可以自动生成路由,代替了下面的路由

          path('books/', views.BookView.as_view()),
          path('books/<int:pk>', views.BookViewDetail.as_view()),

          异:方法二可以拼接路径,如果不拼接是和方法一一样的

          imageimage

      • 视图集
        • 导入:from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
        • views.py
          from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
          class BookView(ModelViewSet):
              queryset = models.Book.objects.all()
              serializer_class = serializer.BookSerializer
        • urls.py
          from django.contrib import admin
          from django.urls import path, include
          from rest_framework.routers import SimpleRouter
          
          from app01 import views
          
          router = SimpleRouter()
          router.register('books', views.BookView, 'books')
          
          urlpatterns = [
              path('admin/', admin.site.urls),
              path('api/v1/',include(router.urls)),
          ]
        • ReadOnlyModelViewSet视图集

          继承该ReadOnlyModelViewSet视图集的作用是只读,只做查询,修改删除等操作不允许

          from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet,ViewSet,GenericViewSet,ViewSetMixin
          class BookView(ReadOnlyModelViewSet):
              queryset = models.Book.objects.all()
              serializer_class = serializer.BookSerializer

          image

        • ModelViewSet与ReadOnlyModelViewSet视图集de总结

          ModelViewSet可以写五个接口,而ReadOnlyModelViewSet只能写两个接口

          • 本质
            • ModelViewSet继承了五个视图扩展类+GenericViewSet,GenericViewSet继承了ViewSetMixin+GenericAPIView

              PS:ViewSetMixin控制了路由写法

            • ReadOnlyModelViewSet继承了RetrieveModelMixin+ListModelMixin+GenericViewSet

        • 其他视图集
          • ViewSet

            ViewSet = ViewSetMixin+APIView

            class ViewSet(ViewSetMixin, views.APIView):
                """
                The base ViewSet class does not provide any actions by default.
                """
                pass
          • GenericViewSet

            GenericViewSet = ViewSetMixin+GenericAPIView

            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

            魔术视图类,控制自动生成路由,可以通过组合继承,以前的写法可以继续使用,但是如果要自动生成路由必须得继承ViewSetMixin及其子类;或者选择继承ViewSet、GenericViewSet

            class ViewSetMixin:
                """
                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'})
                """
            这就是魔法。  
             
            重写' .as_view() ',以便它接受一个' actions '关键字执行  
            将HTTP方法绑定到资源上的动作。  
             
            例如,创建绑定'GET'和'POST'方法的具体视图  
            到“列表”和“创建”动作…  
             
            = MyViewSet视图。 As_view ({'get': 'list', 'post': 'create'})
    • 总结

      • 第一层:基于APIView写视图,get、post、put、delete都需要自己写,序列化的数据和序列化类需要获取后指定
        class BookView(APIView):
            def get(self, requets):
                book_list = models.Book.objects.all()
                ser = serializer.BookSerializer(instance=book_list, many=True)
                return Response(ser.data)
      • 第二层:基于GenericAPIView写视图,优化了视图类内序列化数据和序列化类的代码冗余问题,通过queryset和serializer_class指定序列化集和序列化器即可,一个视图类内写一次即可,最后通过get_queryset和get_serializer方法处理
        class BookView(GenericAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
            def get(self, requets):
                obj = self.get_queryset() 
                ser = self.get_serializer(instance=obj,many=True) 
                return Response(ser.data)
      • 第三层:基于GenericAPIView+5个视图扩展类写视图,每个扩展类对应一个接口,更加细化,通过继承父类(扩展类)减少了代码的冗余
        class BookView(ListModelMixin,CreateModelMixin,GenericAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
            def get(self, request):
               return super().list(request)
      • 第四层,基于九个视图子类写,视图子类将扩展类和GenericAPIView封装到一块,使得我们要写的代码更少了,总之就是牛逼~
        class BookView(ListCreateAPIView):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
      • 第五层,基于ViewSet写视图,这样以来5个接口就都在一个视图类内,代码更少了,但是可扩展性低了,路由也是问题,get所有和get一条路由冲突需要修改
        class BookView(ModelViewSet):
            queryset = models.Book.objects.all()
            serializer_class = serializer.BookSerializer
        '''路由'''
        router = SimpleRouter()
        router.register('books', views.BookView, 'books')
        
        urlpatterns = [
            path('admin/', admin.site.urls),
            path('api/v1/',include(router.urls)),
        ]
    • 视图集中定义附加action动作

      • 视图
        from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
        class StudentModelViewSet(ModelViewSet):
            queryset = Student.objects.all()
            serializer_class = StudentModelSerializer
        
            def login(self,request):
                """学生登录功能"""
                return Response({"message":"登录成功"})
      • 路由
        urlpatterns = [
            path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})),
            re_path("students8/(?P<pk>\d+)/",
                    views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
        
            path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"}))
        
        ]
      • action属性
        from rest_framework.viewsets import ModelViewSet
        from students.models import Student
        from .serializers import StudentModelSerializer
        from rest_framework.response import Response
        class StudentModelViewSet(ModelViewSet):
            queryset = Student.objects.all()
            serializer_class = StudentModelSerializer
        
            def get_new_5(self,request):
                """获取最近添加的5个学生信息"""
                # 操作数据库
                print(self.action) # 获取本次请求的视图方法名
                
                
        通过路由访问到当前方法中.可以看到本次的action就是请求的方法名

路由组件

REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter

    路由组件使用,如果视图类继承了ViewSetMixin及其子类,那么路由写法可以改变,而且视图类中的方法也可以自定制,不一定是get,post,可以随意命名

    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.response import Response
    class TestView(ViewSetMixin,APIView):
        def login(self,requets):
            return Response('test-login')
    		
    		
    # 路由
    path('test/',views.TestView.as_view({'get': 'login'}))

    image

    注意

    # 继承的时候,如果继承了ModelViewSet就相当于继承了ViewSetMixin的子类了,不需要继承ViewSetMixin了,不然会报错
    class BookView(ModelViewSet):
        queryset = models.Book.objects.all()
        serializer_class = serializer.BookSerializer
    
    
    path('books/', views.BookView.as_view({'get':'list'})),
    • 路由是如何映射的?

      1、ViewSetMixin写在前面
      2、先走ViewSetMixin的as_view
              if not actions:
                  raise TypeError("The `actions` argument must be provided when "
                                  "calling `.as_view()` on a ViewSet. For example "
                                  "`.as_view({'get': 'list'})`")
      4、如果actions不传就报错,也就是as_view()内不写字典就报错
      5、view闭包函数
       def view(request, *args, **kwargs):
                  self = cls(**initkwargs)
              ····
          for method, action in actions.items():
              '''method:get     action:login'''
              # 把login方法的内存地址给了handler
              handler = getattr(self, action)
              # 通过反射,设置给get---》对应login---》get请求执行get方法,现在get方法变成了login方法
              setattr(self, method, handler)
            return self.dispatch(request, *args, **kwargs)# 跟之前一样了
    • 继承ModelViewSet,路由写法

      ModelViewSet继承了五个扩展类+GenericViewSet,提供了相应的接口方法增删改查

      from rest_framework.viewsets import ModelViewSet
      class BookView(ModelViewSet):
          queryset = models.Book.objects.all()
          serializer_class = serializer.BookSerializer
      • 自己配路由的映射
        path('books/', views.BookView.as_view({'get':'list','post':'create'})),
        path('books/<int:pk>', views.BookView.as_view({'get':'retrieve','put':'update','delete':'destroy'}))
      • 自动生成路由
        from rest_framework.routers import SimpleRouter
        from app01 import views
        
        router = SimpleRouter()
        router.register('books', views.BookView, 'books')
        urlpatterns = [
        path('api/v1/', include(router.urls)),
        ]
        '''
        或者不写path('api/v1/', include(router.urls)),写下面的
        urlpatterns += router.urls
        '''

        image

    • action装饰器

      只要是继承ViewSetMixin视图类或其子类,都可以加action装饰器

      • 导入:rest_framework.decorators.action
      • 参数:
        • methods:请求方法,列表的形式写
        • detail:是否带id,True不带id,False带id
        • url_path:地址,地址如果不写,默认已方法名为地址
        • url_name:起别名
      • 继承APIView+ViewSetMixin使用装饰器
        from rest_framework.decorators import action
        class TestView(ViewSetMixin,APIView):
            @action(methods=['GET','POST'],detail=False)
            def login(self,requets):
                return Response('test-login')
        from rest_framework.routers import SimpleRouter
        from app01 import views
        
        router1 = SimpleRouter()
        router1.register('test',views.TestView,'test')
        urlpatterns = [
        path('aip/v2/', include(router1.urls)),
        ]
        
        '''
        http://127.0.0.1:8000/aip/v2/test/login/
        '''
      • 注意
        如果这样写detail=True,路由就成了http://127.0.0.1:8000/aip/v2/test/1/login/,数字的部分一般为pk
        最后都是路由前缀拼接一个方法名
    • 路由router形成URL的方式

      • SimpleRouterimage
      • DefaultRouterimage
      • DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据
    • 总结

      • 路由的写法有三种:
        • 手动配置:path('books/<int:pk>', views.BookDetailView.as_view())
        • 继承ViewSetMixin,使用action属性:views.PublishView.as_view({'get':'list','post':'create'}))
        • 使用SimpleRouter或DefaultRouter自动生成(两种方式):
          router = SimpleRouter()
          router.register('books', views.PublishView, 'books')
          urlpatterns = [
          path('api/v1/', include(router.urls)),
          ]
          # 或者
          urlpatterns = [
          urlpatterns+=router.urls
          ]
        • DefaultRouter比SimpleRouter多一层根路径

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/144006.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!