zoukankan      html  css  js  c++  java
  • drf过滤、分页、全局异常处理

    一 过滤Filtering

    详细链接:

    http://www.xuexianqi.top/archives/584.html

    对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

    1
    pip install django-filter

    在配置文件中增加过滤后端的设置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    INSTALLED_APPS = [
    ...
    'django_filters', # 需要注册应用,
    ]

    REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
    }

    在视图中添加filter_fields属性,指定可以过滤的字段

    1
    2
    3
    4
    5
    6
    classStudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    filter_fields = ('age', 'sex')

    # 127.0.0.1:8000/four/students/?sex=1

    二 排序

    对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。

    使用方法:

    在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。

    前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    classStudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ('id', 'age')

    # 127.0.0.1:8000/books/?ordering=-age
    # -id 表示针对id字段进行倒序排序
    # id 表示针对id字段进行升序排序

    如果需要在过滤以后再次进行排序,则需要两者结合!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from rest_framework.generics import ListAPIView
    from students.models import Student
    from .serializers import StudentModelSerializer
    from django_filters.rest_framework import DjangoFilterBackend
    class Student3ListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age', 'sex')
    # 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
    # 否则过滤功能会失效
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    ordering_fields = ('id', 'age')

    三 分页Pagination

    REST framework提供了分页的支持。

    我们可以在配置文件中设置全局的分页方式,如:

    1
    2
    3
    4
    REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100 # 每页数目
    }

    也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。

    1
    2
    3
    4
    5
    6
    7
    8
    classLargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000
    class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = LargeResultsSetPagination

    注意:如果在视图内关闭分页功能,只需在视图内设置

    1
    pagination_class = None

    可选分页器

    1) PageNumberPagination

    前端访问网址形式:

    1
    GET  http://127.0.0.1:8000/students/?page=4

    可以在子类中定义的属性:

    • page_size 每页数目
    • page_query_param 前端发送的页数关键字名,默认为”page”
    • page_size_query_param 前端发送的每页数目关键字名,默认为None
    • max_page_size 前端最多能设置的每页数量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    # APIView
    from rest_framework.pagination import PageNumberPagination
    # 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size无效
    classPager(APIView):
    defget(self,request,*args,**kwargs):
    # 获取所有数据
    ret=models.Book.objects.all()
    # 创建分页对象
    page=PageNumberPagination()
    # 在数据库中获取分页的数据
    page_list=page.paginate_queryset(ret,request,view=self)
    # 对分页进行序列化
    ser=BookSerializer1(instance=page_list,many=True)
    return Response(ser.data)
    # 二 自定制 url=http://127.0.0.1:8000/pager/?page=2&size=3
    # size=30,无效,最多5条
    classMypage(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    # 定制传参
    page_size_query_param = 'size'
    # 最大一页的数据
    max_page_size = 5
    classPager(APIView):
    defget(self,request,*args,**kwargs):
    # 获取所有数据
    ret=models.Book.objects.all()
    # 创建分页对象
    page=Mypage()
    # 在数据库中获取分页的数据
    page_list=page.paginate_queryset(ret,request,view=self)
    # 对分页进行序列化
    ser=BookSerializer1(instance=page_list,many=True)
    # return Response(ser.data)
    # 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可)
    return page.get_paginated_response(ser.data)

    #ListAPIView

    # 声明分页的配置类
    from rest_framework.pagination import PageNumberPagination
    classStandardPageNumberPagination(PageNumberPagination):
    # 默认每一页显示的数据量
    page_size = 2
    # 允许客户端通过get参数来控制每一页的数据量
    page_size_query_param = "size"
    max_page_size = 10
    # 自定义页码的参数名
    page_query_param = "p"

    classStudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = StandardPageNumberPagination

    # 127.0.0.1/four/students/?p=1&size=5

    2)LimitOffsetPagination

    前端访问网址形式:

    1
    GET http://127.0.0.1/four/students/?limit=100&offset=400

    可以在子类中定义的属性:

    • default_limit 默认限制,默认值与PAGE_SIZE设置一直
    • limit_query_param limit参数名,默认’limit’
    • offset_query_param offset参数名,默认’offset’
    • max_limit 最大limit限制,默认None
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # APIView
    # http://127.0.0.1:8000/pager/?offset=4&limit=3
    from rest_framework.pagination import LimitOffsetPagination
    # 也可以自定制,同简单分页
    classPager(APIView):
    defget(self,request,*args,**kwargs):
    # 获取所有数据
    ret=models.Book.objects.all()
    # 创建分页对象
    page=LimitOffsetPagination()
    # 在数据库中获取分页的数据
    page_list=page.paginate_queryset(ret,request,view=self)
    # 对分页进行序列化
    ser=BookSerializer1(instance=page_list,many=True)
    # return page.get_paginated_response(ser.data)
    return Response(ser.data)
    #ListAPIView
    from rest_framework.pagination import LimitOffsetPagination
    classStandardLimitOffsetPagination(LimitOffsetPagination):
    # 默认每一页查询的数据量,类似上面的page_size
    default_limit = 2
    limit_query_param = "size"
    offset_query_param = "start"

    classStudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 调用页码分页类
    # pagination_class = StandardPageNumberPagination
    # 调用查询偏移分页类
    pagination_class = StandardLimitOffsetPagination

    3)CursorPagination

    前端访问网址形式:

    1
    GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D

    可以在子类中定义的属性:

    • cursor_query_param:默认查询字段,不需要修改
    • page_size:每页数目
    • ordering:按什么排序,需要指定
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #APIView
    from rest_framework.pagination import CursorPagination
    # 看源码,是通过sql查询,大于id和小于id
    classPager(APIView):
    defget(self,request,*args,**kwargs):
    # 获取所有数据
    ret=models.Book.objects.all()
    # 创建分页对象
    page=CursorPagination()
    page.ordering='nid'
    # 在数据库中获取分页的数据
    page_list=page.paginate_queryset(ret,request,view=self)
    # 对分页进行序列化
    ser=BookSerializer1(instance=page_list,many=True)
    # 可以避免页码被猜到
    return page.get_paginated_response(ser.data)
    # ListAPIView
    classMyCursorPagination(CursorPagination):
    page_size=2
    ordering='-id'
    from rest_framework.generics import ListAPIView
    classAuthorListView(ListAPIView):
    serializer_class = serializers.AuthorModelSerializer
    queryset = models.Author.objects.filter(is_delete=False)
    pagination_class =MyCursorPagination

    应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
    class MyPageNumberPagination(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    # 定制传参
    page_size_query_param = 'size'
    # 最大一页的数据
    max_page_size = 5

    classMyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2
    # 最大一页的数据
    max_limit = 5
    classMyCursorPagination(CursorPagination):
    page_size=2
    ordering='-id'
    from rest_framework.generics import ListAPIView
    classAuthorListView(ListAPIView):
    serializer_class = serializers.AuthorModelSerializer
    queryset = models.Author.objects.filter(is_delete=False)
    pagination_class =MyCursorPagination

    分页器三种如何使用

    1 内置了三种分页器
        -PageNumberPagination:普通分页
        -LimitOffsetPagination:偏移分页
        -CursorPagination:游标分页
    2 APIView和GenericAPIView+ListModelMixin
    
    3 GenericAPIView+ListModelMixin的分页模式
        
    4 PageNumberPagination:普通分页(用的最多)
        -page_size = api_settings.PAGE_SIZE  # 每页显示多少条
        -page_query_param = 'page'           # 查询参数
        -page_size_query_param = size        # 查询的时候指定每页显示多少条
        -max_page_size = 10                # 每页最多显示多少条
        
        -使用方式:
            -定义一个类,继承PageNumberPagination
            -重写四个属性
            -在继承了GenericAPIView+ListModelMixin视图类中配置
                pagination_class = MyPageNumberPagination
            -查询
                http://127.0.0.1:8000/students/?page=1&size=5
                
    5 LimitOffsetPagination:偏移分页
        -default_limit = api_settings.PAGE_SIZE  # 默认条数
        -limit_query_param = 'limit'             # 查询时,指定查询多少条
        -offset_query_param = 'offset'           # 查询时,指定的起始位置是哪 
        -max_limit = None                        # 查询时,最多返回多少条
        -使用方式:
            -定义一个类,继承LimitOffsetPagination
            -重写四个属性
            -在继承了GenericAPIView+ListModelMixin视图类中配置
                pagination_class = MyPageNumberPagination
            -查询
            http://127.0.0.1:8000/students/?limit=100&offset=1
    6 CursorPagination:游标分页(速度块)
        -cursor_query_param = 'cursor'  # 查询的时候,指定的查询方式
        -page_size = api_settings.PAGE_SIZE # 每页显示多少条
        -ordering = '-created'   # 排序方式
        -page_size_query_param = size  # 查询的时候指定每页显示多少条
        -max_page_size = None          #每页最多显示多少条
        -使用方式:
                -定义一个类,继承LimitOffsetPagination
                -重写四个属性
                -在继承了GenericAPIView+ListModelMixin视图类中配置
                    pagination_class = MyPageNumberPagination
                -查询
                http://127.0.0.1:8000/students/?limit=100&offset=1
                        
                        
     7 APIView的分页模式
        -新建一个类,继承普通分页,重写四个属性
        -视图类写法如下
        class StudentApiView(APIView):
            def get(self,request):
                student_list=Student.objects.all()
                page=MyPageNumberPagination()# 实例化得到对象
                # 只需要换不同的分页类即可
                res=page.paginate_queryset(student_list,request,self)# 开始分页
                ser=StudentSerializer(res,many=True)
                return page.get_paginated_response(ser.data) # 返回数据
    View Code

    源码理解:

    https://www.cnblogs.com/Franciszw/p/13341191.html#6%E8%BF%87%E6%BB%A4%E6%8E%92%E5%BA%8F%E5%88%86%E9%A1%B5%E8%87%AA%E5%8A%A8%E7%94%9F%E6%88%90%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3

    四 异常处理 Exceptions

    REST framework提供了异常处理,我们可以自定义异常处理函数。

    4.1 使用方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from rest_framework.views import exception_handler

    defcustom_exception_handler(exc, context):
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response = exception_handler(exc, context)

    # 在此处补充自定义的异常处理
    if response isNone:
    response.data['status_code'] = response.status_code

    return response

    在配置文件中声明自定义的异常处理

    1
    2
    3
    REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
    }

    如果未声明,会采用默认的方式,如下

    rest_frame/settings.py

    1
    2
    3
    REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
    }

    4.2 案例

    补充上处理关于数据库的异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    from rest_framework.views import exception_handler as drf_exception_handler
    from rest_framework import status
    from django.db import DatabaseError

    def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
    view = context['view']
    print('[%s]: %s' % (view, exc))
    if isinstance(exc, DatabaseError):
    response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
    else:
    response = Response({'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return response

    # 在setting.py中配置
    REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.ser.exception_handler'
    }

    4.3 REST framework定义的异常

    • APIException 所有异常的父类
    • ParseError 解析错误
    • AuthenticationFailed 认证失败
    • NotAuthenticated 尚未认证
    • PermissionDenied 权限决绝
    • NotFound 未找到
    • MethodNotAllowed 请求方式不支持
    • NotAcceptable 要获取的数据格式不支持
    • Throttled 超过限流次数
    • ValidationError 校验失败

    也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

    自动生成接口文档

    1 借助于第三方:coreapi,swagger
    2 在路由中
        from rest_framework.documentation import include_docs_urls
        path('docs/', include_docs_urls(title='图书管理系统api'))
    3 在配置文件中
        REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
        }
    4 写视图类(需要加注释)
        class BookListCreateView(ListCreateAPIView):
            """
            get:
            返回所有图书信息.
            asdfasfda
    
            post:
            新建图书.
            """
            queryset = Student.objects.all()
            serializer_class = StudentSerializer
    5 只需要在浏览器输入,就可以看到自动生成的接口文档()
        http://127.0.0.1:8000/docs/
    View Code

     drf过滤源码解析

    https://daimajiaoliu.com/daima/47240473f9003e8

    全局异常

    1 统一接口的返回方式,即便视图函数执行出错
    2 使用方式
        -写一个函数
        def common_exception_handler(exc, context):
            response = exception_handler(exc, context)
            if response is None:
                response = Response({'code':999,'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
            return response
           -在setting中配置
            REST_FRAMEWORK = {
                'EXCEPTION_HANDLER':'app01.utils.common_exception_handler'
            }
    View Code
    每天逼着自己写点东西,终有一天会为自己的变化感动的。这是一个潜移默化的过程,每天坚持编编故事,自己不知不觉就会拥有故事人物的特质的。 Explicit is better than implicit.(清楚优于含糊)
  • 相关阅读:
    Java生鲜电商平台-生鲜电商数据分析思维以及指标(小程序/APP)
    Java生鲜电商平台-生鲜电商中售后退款流程整理与架构实现(小程序/APP)
    Java生鲜电商平台-优惠券功能设计与开发(小程序/APP)
    Java生鲜电商平台-SpringCloud微服务架构中分布式事务解决方案
    Java生鲜电商平台-深入理解微服务SpringCloud各个组件的关联与架构
    Java生鲜电商平台-系统架构图
    Java生鲜电商平台-生鲜电商中微服务架构与集群模式如何选择?(小程序/APP)
    Java生鲜电商平台-生鲜电商微服务积分商城系统架构设计(小程序/APP)
    Java生鲜电商平台-微服务电商优惠券的架构设计(小程序/APP)
    Java生鲜电商平台-生鲜电商中优惠券的设计 (小程序/APP)
  • 原文地址:https://www.cnblogs.com/kylin5201314/p/14938795.html
Copyright © 2011-2022 走看看