python-DRF_自定制过滤器_分页器Pagination_异常处理_封装Response对象_自动生成接口文档

9 篇文章 1 订阅

DRF-Django rest framework

1. 自定制过滤器

# 自定制过滤器 (源码流程)
	查询所有才会有过滤  
    ==>  list才需要过滤
    ==>  queryset = self.filter_queryset(self.get_queryset())  
    ==>  GenericAPIView  
    ==>  filter_queryset
    
# filter_queryset方法
    def filter_queryset(self, queryset):
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset

# 使用方法        
    1. 写一个类MyFilter,继承BaseFilterBackend
    2. 重写filter_queryset方法,在该方法内部进行过滤(自己设置的过滤条件)
    3. 返回queryset对象(过滤后的queryset对象)
    4. 配置在视图类中
        filter_backends = [MyFilter,]	

2. 分页器Pagination

1. 分页介绍

1. 内置了三种分页器
	1. PageNumberPagination   普通分页
    2. LimitOffsetPagination  偏移分页
    3. CursorPagination       游标分页
    
2. 分页模式
	1. APIView
    2. GenericAPIView + ListModelMixin
    
3. 分页器使用 
	1. 一个视图函数只能使用一个分页器    	
    2. 如果在视图内关闭分页功能,只需在视图内设置 pagination_class = None

2. 可选分页器

1. PageNumberPagination
# 前端访问网址形式
	GET  http://127.0.0.1:8000/students/?page=4&size=3
            
# 可以在子类中定义的属性
    page_size              每页数目
    page_query_param       前端发送的页数关键字名,默认为”page”
    page_size_query_param  前端发送的每页数目关键字名,默认为None
    max_page_size          前端最多能设置的每页数量
    
	page_size = api_settings.PAGE_SIZE  # 每页显示多少条
    page_query_param = 'page'           # 查询参数
    page_size_query_param = size        # 查询的时候指定每页显示多少条
    max_page_size = 10                  # 每页最多显示多少条
    
# 使用方式
    1. 定义一个类,继承PageNumberPagination
    2. 重写四个属性
    3. 在继承了GenericAPIView + ListModelMixin视图类中配置
    	pagination_class = MyPageNumberPagination
    4. 查询
    	http://127.0.0.1:8000/students/?page=1&size=5    
                
# 示例
class MyPageNumberPagination(PageNumberPagination):
    page_size = 3
    page_query_param = 'page'
    page_size_query_param = 'size'
    max_page_size = 5

# GenericAPIView + ListModelMixin的分页模式  
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 普通分页
    pagination_class = MyPageNumberPagination    
2. LimitOffsetPagination
# 前端访问网址形式
	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

    default_limit = api_settings.PAGE_SIZE  # 默认条数
    limit_query_param = 'limit'             # 查询时,指定查询多少条
    offset_query_param = 'offset'           # 查询时,指定的起始位置是哪 
    max_limit = None                        # 查询时,最多返回多少条
    
# 使用方式
    1. 定义一个类,继承LimitOffsetPagination
    2. 重写四个属性
    3. 在继承了GenericAPIView + ListModelMixin视图类中配置
    	pagination_class = MyLimitOffsetPagination
    4. 查询
    	http://127.0.0.1:8000/students/?limit=100&offset=1
                
# 示例
class MyLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2              # 默认条数
    limit_query_param = 'limit'    # 查询时,指定查询多少条
    offset_query_param = 'offset'  # 查询时,指定的起始位置是哪
    max_limit = 5                  # 查询时,最多返回多少条
    
# GenericAPIView + ListModelMixin的分页模式    
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 偏移分页
    pagination_class = MyLimitOffsetPagination 
3. CursorPagination
# 前端访问网址形式
	GET  http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
        
# 可以在子类中定义的属性
    cursor_query_param  默认查询字段,不需要修改
    page_size           每页数目
    ordering            按什么排序,需要指定
    
    cursor_query_param = 'cursor'       # 查询的时候,指定的查询方式
    page_size = api_settings.PAGE_SIZE  # 每页显示多少条
    ordering = '-created'               # 排序方式 一定要指定排序方式 model中的可以排序的字段
    page_size_query_param = size        # 查询的时候指定每页显示多少条
    max_page_size = None                # 每页最多显示多少条
    
# 使用方式
    1. 定义一个类,继承CursorPagination
    2. 重写四个属性
    3. 在继承了GenericAPIView + ListModelMixin视图类中配置
    	pagination_class = MyCursorPagination
    4. 查询
    	http://127.0.0.1:8000/students/
              
# 示例
class MyCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'     # 查询的时候,指定的查询方式
    page_size = 2                     # 每页显示多少条
    ordering = 'id'                   # 排序方式 就是model中的可以排序的字段
    page_size_query_param = 'size'    # 查询的时候指定每页显示多少条
    max_page_size = 5                 # 每页最多显示多少条
    
# GenericAPIView + ListModelMixin的分页模式    
# class StudentView(GenericViewSet,ListAPIView):
class StudentView(GenericViewSet,ListModelMixin):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    # 游标分页
    pagination_class = MyCursorPagination
4. APIView分页模式
1. 新建一个类,继承普通分页,重写四个属性
    class MyPageNumberPagination(PageNumberPagination):
        page_size = 3
        page_query_param = 'page'
        page_size_query_param = 'size'
        max_page_size = 5

    class MyLimitOffsetPagination(LimitOffsetPagination):
        default_limit = 2              # 默认条数
        limit_query_param = 'limit'    # 查询时,指定查询多少条
        offset_query_param = 'offset'  # 查询时,指定的起始位置是哪
        max_limit = 5                  # 查询时,最多返回多少条

    class MyCursorPagination(CursorPagination):
        cursor_query_param = 'cursor'   # 查询的时候,指定的查询方式
        page_size = 2                   # 每页显示多少条
        ordering = 'id'                 # 排序方式 model: id
        page_size_query_param = 'size'  # 查询的时候指定每页显示多少条
        max_page_size = 5               # 每页最多显示多少条

2. 视图类写法如下
    class StudentApiView(APIView):
        def get(self,request):
            # 获取所有数据
            student_list=Student.objects.all()

            # 进行分页 实例化得到分页对象
            page = MyPageNumberPagination()   # 普通分页
            page = MyLimitOffsetPagination()  # 偏移分页
            page = MyCursorPagination()       # 游标分页

            # 在数据库中获取分页的数据
            res=page.paginate_queryset(student_list,request,self)

            # 对分页进行序列化
            ser=StudentSerializer(res,many=True)

            # 方式一: 把分页后的数据放到响应中 
            return Response(ser.data)

            # 方式二: 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数
            return page.get_paginated_response(ser.data)

3. 异常处理 Exceptions

1. 使用方式

from rest_framework.views import exception_handler

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

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

    return response

# 在配置文件中声明自定义的异常处理
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.utils.common_exception_handler'
}

# 如果未声明,会采用默认的方式
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

2. 示例

1. 统一接口的返回方式,即便视图函数执行出错

2. 使用方式
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 common_exception_handler(exc, context):
    response = 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

# 统一接口的返回方式
def common_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if response is None:
        print(context['view'])  # 哪个视图函数出错
        view = context['view']
        request = context['request']
        print(context['request'])  # 当此请求的request对象
        print(str(view))
        print(request.path)
        print(request.method)
        print(request.META.get('REMOTE_ADDR'))
        response = Response({'code':999,'detail': '未知错误'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    return response
    
# 在setting中配置
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER':'app01.utils.common_exception_handler'
    }

3. DRF定义的异常

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

4. 封装Response对象

1. 自己封装的response
class APIResponse(Response):
    def __init__(self, code=100, msg='成功', data=None, status=None, headers=None, content_type=None, **kwargs):
        dic = {'code': code, 'msg': msg}
        if data:
            dic['data'] = data

        dic.update(kwargs) # 这里使用update
        super().__init__(data=dic, status=status,
                         template_name=None, headers=headers,
                         exception=False, content_type=content_type)
        
2. 使用
from app01.utils import APIResponse
class StudentApiView(APIView):
	# 不封装的方式
    def get(self,request):
         res={'code':100,'msg':'成功','data':None}
         student_list=Student.objects.all()
         ser=StudentSerializer(student_list,many=True)
         res['data']=ser.data
         return Response(res)
    
    # 封装的方式
    def get(self, request):
        student_list = Student.objects.all()
        ser = StudentSerializer(student_list, many=True)
        return APIResponse(code=100,msg='查询成功',data=ser.data,count=200,next='http://wwwa.asdfas')

5. 自动生成接口文档

1. 安装依赖

# 借助于第三方 (swagger 也可以)
	pip install coreapi

2. 设置接口文档访问路径

1. 在总路由中添加接口文档路径

2. 文档路由对应的视图配置为rest_framework.documentation.include_docs_urls

3. 参数title为接口文档网站的标题

# 示例
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站点页面标题'))
]

3. 文档描述说明的定义位置

# 写视图类(需要加注释)

1. 单一方法的视图,可直接使用类视图的文档字符串
    class BookListView(ListAPIView):
        """
        返回所有图书信息.
   		"""
        
2. 包含多个方法的视图,在类视图的文档字符串中,分开方法定义
	class BookListCreateView(ListCreateAPIView):
        """
        get:
        返回所有图书信息.

        post:
        新建图书.
        """

3. 对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分
	class BookInfoViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
        """
        list:
        返回图书列表数据

        retrieve:
        返回图书详情数据

        latest:
        返回最新的图书数据

        read:
        修改图书的阅读量
        """

4. Settings配置

# 没有配置的报错信息
	AttributeError: 'AutoSchema' object has no attribute 'get_link' 

REST_FRAMEWORK = {
	'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}

5. 访问接口文档网页

浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档

6. 注意点

1. 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read

2. 参数的Description需要在模型类或序列化器类的字段中以help_text选项定义

# 模型类
class Student(models.Model):
    ...
    age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
    ...
 
# 序列化器类
class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        extra_kwargs = {
            'age': {
                'required': True,
                'help_text': '年龄'
            }
        }    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I believe I can fly~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值