zoukankan      html  css  js  c++  java
  • DRF之视图

    一、基于APIView的五个接口

    首先,APIView是Rest Framework提供的所有视图的基类,继承了Django的View

    其次,APIView和View的不同在于:

    • 他们传入视图方法中的request对象不是同一个,APIView的是Request对象,View的是HttpResponse对象。
    • APIView返回的是Response对象,是符合前端Accept要求的格式
    • 在dispatch分发前会进行身份认证,权限检查,流量控制

    五个接口实例:

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    # 基于APIView
    class BooksView(APIView):
        # 查所有
        def get(self, request):
            books = Books.objects.all()
            books_ser = BooksSerializers(instance=books, many=True)
            return Response(books_ser.data)
    	
        # 增加
        def post(self, request):
            books_ser = BooksSerializers(data=request.data)
            if books_ser.is_valid():
                books_ser.save()
            return Response(books_ser.data)
    
    
    class BookView(APIView):
        # 查单个
        def get(self, request, pk):
            print(pk, type(pk))
            book = Books.objects.filter(pk=pk).first()
            book_ser = BooksSerializers(instance=book)
            return Response(book_ser.data)
    
    	# 修改
        def put(self, request, pk):
            book = Books.objects.filter(pk=pk).first()
            book_ser = BooksSerializers(instance=book, data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'message': book_ser.errors})
    
    	# 删除
        def delete(self, request, pk):
            Books.objects.filter(pk=pk).delete()
            return Response({'message': '删除成功'})
        
        
    # urls.py
    path('books/', views.BooksView.as_view()),
    re_path(r'^book/(?P<pk>d+)/', views.BookView.as_view()),
    

    二、基于GenericAPIView的五个接口

    首先,GenericAPIView继承了APIView,并增加了操作序列化器和数据库查询方法

    并提供了两个属性和两个方法

    queryset:指要传的queryset对象

    get_queryset():返回queryset对象

    serializer_class:指明视图使用的序列化器,也就是哪个序列化类来序列化这堆数据

    get_serializer():返回序列化器类

    get_object():返回视图所需的模型类数据对象

    源码如下:

    # from rest_framework.generics import GenericAPIView
    class GenericAPIView(views.APIView):
        queryset = None				# 指要传的queryset对象
        serializer_class = None		# 指明视图使用的序列化器,也就是哪个序列化类来序列化这堆数据
        ......
        
    	def get_queryset(self):
        	......
            queryset = self.queryset
            # 判断传入的queryset是不是QuerySet的对象
            if isinstance(queryset, QuerySet):
                queryset = queryset.all()	# 是就加all()——>Book.objects.all()
            return queryset		# 返回queryset对象
    
    
    	def get_serializer(self, *args, **kwargs):
            serializer_class = self.get_serializer_class()
            
            # 向序列化器对象的context属性补充三个数据,
            # request,当前视图的请求对象
            # format,当前请求的类视图对象
            # view,当前请求期望返回的数据格式
            kwargs.setdefault('context', self.get_serializer_context())
            return serializer_class(*args, **kwargs)	# 返回序列化器类
    

    五个接口实例:

    from rest_framework.generics import GenericAPIView
    from rest_framework.response import Response
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    # 基于GenericAPIView
    class BooksView(GenericAPIView):
        queryset = Books.objects
        serializer_class = BooksSerializers
    	
        # 查所有
        def get(self, request):
            books = self.get_queryset() # 相当于Books.objects.all()
            books_ser = self.get_serializer(instance=books, many=True)
            return Response(books_ser.data)
    	
        # 新增
        def post(self, request):
            books_ser = self.get_serializer(data=request.data)
            if books_ser.is_valid():
                books_ser.save()
            return Response(books_ser.data)
    
    
    class BookView(GenericAPIView):
        queryset = Books.objects
        serializer_class = BooksSerializers
    
    	# 查单个
        def get(self, request, pk):
            book = self.get_object()
            book_ser = self.get_serializer(instance=book)
            return Response(book_ser.data)
    
    	# 修改
        def put(self, request, pk):
            book = self.get_object()
            book_ser = self.get_serializer(instance=book, data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return Response(book_ser.data)
            else:
                return Response({'message': book_ser.errors})
    
    	# 删除
        def delete(self, request, pk):
            self.get_object().delete()
            return Response({'message': '删除成功'})
    

    三、GenericAPIView的五个视图括展类

    因为之前代码的处理流程重复代码太多,所以可以使用相应的扩展类来减少编写的代码量。

    而这五个扩展类需要搭配GenericAPIView父类,并且还要调用GenericAPIView提供的序列化器和数据查询方法。

    ListModeMixin

    列表视图扩展类(类似于查询所有),提供了快速获取所有列表的视图,并且该方法会对数据进行过滤和分页,成功返回200

    源代码:

    class ListModelMixin:
        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)	# 返回序列化后的数据
    

    CreateModeMixin

    创建视图扩展类(创建数据),提供了快速创建资源的视图,成功返回201,失败400

    原代码:

    class CreateModelMixin:
        # 创建模型实例
        def create(self, request, *args, **kwargs):
            # 获取序列化器
            serializer = self.get_serializer(data=request.data)
            # 验证
            serializer.is_valid(raise_exception=True)
            # 保存
            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 {}
    

    RetrieveModelMixin

    详情视图扩展类(查询单个),返回一个存在的数据对象,成功返回201,失败404

    原代码:

    class RetrieveModelMixin:
        def retrieve(self, request, *args, **kwargs):
            # 获取对象,并检查对象的权限
            instance = self.get_object()
            # 序列化
            serializer = self.get_serializer(instance)
            return Response(serializer.data)
    

    UpdateModelMixin

    更新视图扩展类(更新数据),快速更新一个存在的数据对象,也可实现局部更新 partial_update() 成功返回201,失败400

    源代码

    class UpdateModelMixin:
        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)
    

    DestroyModelMixin

    删除视图扩展类(删除数据),快速删除一个存在的数据对象,成功返回204,不存在返回404。

    源代码

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

    五个视图扩展类的使用实例

    from rest_framework.generics import GenericAPIView
    from app01.ser import BooksSerializers
    from app01.models import Books
    from rest_framework.mixins import ListModelMixin, CreateModelMixin
    from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
    
    
    # 基于GenericAPIView的五个视图扩展类
    class BooksView(GenericAPIView, ListModelMixin, CreateModelMixin):
        queryset = Books.objects
        serializer_class = BooksSerializers
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    
    class BookView(GenericAPIView, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin):
        queryset = Books.objects
        serializer_class = BooksSerializers
    
        def get(self, request, pk):
            return self.retrieve(request)
    
    
        def put(self, request, pk):
            return self.update(request)
    
    
        def delete(self, request, pk):
            return self.destroy(request)
    

    四、GenericAPIView的视图子类

    二话不说先来看其中一个源码!!!其他的子类都是一样的原理

    源码

    # 首先它继承了CreateModelMixin创建视图扩展类
    class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):	
        # 并且它提供了post方法,那我们就不用在视图中写post方法了,这里直接写好了直接调用self.create
        # 只需要传入对象和序列化器就可以了
        def post(self, request, *args, **kwargs):
            return self.create(request, *args, **kwargs)
    

    实例:

    from rest_framework.generics import ListAPIView, UpdateAPIView
    from rest_framework.generics import CreateAPIView, DestroyAPIView, RetrieveAPIView
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    # 基于GenericAPIView的五个视图子类
    class BooksView(ListAPIView, UpdateAPIView):    # 获取所有,修改一个
        queryset = Books.objects
        serializer_class = BooksSerializers
    
    
    # 获取一个,更新一个,删除一个
    class BookView(CreateAPIView, DestroyAPIView, RetrieveAPIView):
        queryset = Books.objects
        serializer_class = BooksSerializers
    

    GenericAPIView的视图子类除了这五个还有四个,也就是这五个方法的其他组合:

    • ListCreateAPIView
      • 提供get、post方法,是 ListAPIView 和 UpdateAPIView 的组合
    • RetrieveDestroyAPIView
      • 提供get、delete方法,是 RetrieveAPIView 和 DestroyAPIView 的组合
    • RetrieveUpdateAPIView
      • 提供get、update方法,是 RetrieveAPIView 和 UpdateAPIView 的组合
    • RetrieveUpdateDestroyAPIView
      • 提供get、update、delete方法,是 RetrieveAPIView 、 UpdateAPIView 和 DestroyAPIView 的组合

    五、ModelViewSet

    首先它继承了 GenericViewSet,并且还有 ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

    ModelViewSet五个接口

    先来看一下它的使用:

    # views.py
    from rest_framework.viewsets import ModelViewSet
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    class BooksView(ModelViewSet):		# 五个接口都有
        queryset = Books.objects
        serializer_class = BooksSerializers
        
        
    # urls.py
    from django.contrib import admin
    from django.urls import path, re_path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        # 当路径匹配,又是get请求,会执行BooksView的list方法
        path('books/', views.BooksView.as_view({'get': 'list', 'post': 'create'})),
        re_path(r'^book/(?P<pk>d+)/', views.BooksView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
    ]
    

    源码分析

    由于 ModelViewSet 继承了 GenericViewSet ,并且 GenericViewSet 又继承了 ViewSetMixin,ViewSetMixin中又重写了 as_view 方法, 也就是说当路径匹配上了会执行这里重写的 as_view 方法。

    分析: as_view 方法

    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        
    	...
    
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            
            # 核心代码,所有路由中只要配置了对应关系,比如{'get': 'list'},当get请求来,就会执行list方法
            for method, action in actions.items():
                # 遍历我们传入的对应关系
                # method = get
                # action = list
                
                # 并且通过反射获取list方法的内存地址
                handler = getattr(self, action)		# handler已经是list的内存地址
                
                # 点拦截(对象.get = list)
                setattr(self, method, handler)
                
            # 当循环完毕之后,对象.get:对应list方法,对象.post:对应create方法
    
            ...
    
            # 后面照常继续
            return self.dispatch(request, *args, **kwargs)
    

    继承ViewSetMixin的视图类

    由上面的分析可以得出,我们也可以直接继承 ViewSetMixin 写视图,如下:

    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.response import Response
    from app01.ser import BooksSerializers
    from app01.models import Books
    
    
    class BooksView(ViewSetMixin, APIView):
        def get_all_books(self, request):
            book_list = Books.objects.all()
            book_ser = BooksSerializers(book_list, many=True)
            return Response(book_ser.data)
        
        
    # urls.py
    # 当匹配上了路由,又是get请求,就调用get_all_books方法
    path('books/', views.BooksView.as_view({'get': 'get_all_books'})),
    
    学习之旅
  • 相关阅读:
    springboot入门系列(一):简单搭建springboot项目
    springboot入门系列(二):SpringBoot整合Swagger
    springboot入门系列(三):SpringBoot教程之RabbitMQ示例
    springboot入门系列(四):SpringBoot和Mybatis配置多数据源连接多个数据库
    Linux下安装RabbitMQ
    Mybatis原理之数据源和连接池
    springboot入门系列(五):SpringBoot连接多RabbitMQ源
    jsp中<c:foreach>分页标签的序号问题
    Java中删除一个文件夹下的所有文件(包括子目录内的文件)
    接口的幂等性
  • 原文地址:https://www.cnblogs.com/XiaoYang-sir/p/14988276.html
Copyright © 2011-2022 走看看