过滤、排序、分页、异常处理
一 过滤Filtering
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。
pip install django-filter
在配置文件中增加过滤后端的设置:
INSTALLED_APPS = [...'django_filters', # 需要注册应用,
]REST_FRAMEWORK = {...'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
在视图中添加filter_fields属性,指定可以过滤的字段
class StudentListView(ListAPIView):queryset = Student.objects.all()serializer_class = StudentSerializerfilter_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中指明。
示例:
class StudentListView(ListAPIView):queryset = Student.objects.all()serializer_class = StudentModelSerializerfilter_backends = [OrderingFilter]ordering_fields = ('id', 'age')# 127.0.0.1:8000/books/?ordering=-age
# -id 表示针对id字段进行倒序排序
# id 表示针对id字段进行升序排序
如果需要在过滤以后再次进行排序,则需要两者结合!
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 = StudentModelSerializerfilter_fields = ('age', 'sex')# 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,# 否则过滤功能会失效filter_backends = [OrderingFilter,DjangoFilterBackend]ordering_fields = ('id', 'age')
三 分页Pagination
REST framework提供了分页的支持。
我们可以在配置文件中设置全局的分页方式,如:
REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination','PAGE_SIZE': 100 # 每页数目
}
也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas
属性来指明。
class LargeResultsSetPagination(PageNumberPagination):page_size = 1000page_size_query_param = 'page_size'max_page_size = 10000
class BookDetailView(RetrieveAPIView):queryset = BookInfo.objects.all()serializer_class = BookInfoSerializerpagination_class = LargeResultsSetPagination
注意:如果在视图内关闭分页功能,只需在视图内设置
pagination_class = None
可选分页器
1) PageNumberPagination
前端访问网址形式:
GET http://127.0.0.1:8000/students/?page=4
可以在子类中定义的属性:
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为”page”
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
# APIView
from rest_framework.pagination import PageNumberPagination
# 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size无效
class Pager(APIView):def get(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条
class Mypage(PageNumberPagination):page_size = 2page_query_param = 'page'# 定制传参page_size_query_param = 'size'# 最大一页的数据max_page_size = 5
class Pager(APIView):def get(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
class StandardPageNumberPagination(PageNumberPagination):# 默认每一页显示的数据量page_size = 2# 允许客户端通过get参数来控制每一页的数据量page_size_query_param = "size"max_page_size = 10# 自定义页码的参数名page_query_param = "p"class StudentAPIView(ListAPIView):queryset = Student.objects.all()serializer_class = StudentModelSerializerpagination_class = StandardPageNumberPagination# 127.0.0.1/four/students/?p=1&size=5
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
# APIView
# http://127.0.0.1:8000/pager/?offset=4&limit=3
from rest_framework.pagination import LimitOffsetPagination
# 也可以自定制,同简单分页
class Pager(APIView):def get(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
class StandardLimitOffsetPagination(LimitOffsetPagination):# 默认每一页查询的数据量,类似上面的page_sizedefault_limit = 2limit_query_param = "size"offset_query_param = "start"class StudentAPIView(ListAPIView):queryset = Student.objects.all()serializer_class = StudentModelSerializer# 调用页码分页类# pagination_class = StandardPageNumberPagination# 调用查询偏移分页类pagination_class = StandardLimitOffsetPagination
3)CursorPagination
前端访问网址形式:
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
可以在子类中定义的属性:
- cursor_query_param:默认查询字段,不需要修改
- page_size:每页数目
- ordering:按什么排序,需要指定
#APIView
from rest_framework.pagination import CursorPagination
# 看源码,是通过sql查询,大于id和小于id
class Pager(APIView):def get(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
class MyCursorPagination(CursorPagination):page_size=2ordering='-id'
from rest_framework.generics import ListAPIView
class AuthorListView(ListAPIView):serializer_class = serializers.AuthorModelSerializerqueryset = models.Author.objects.filter(is_delete=False)pagination_class =MyCursorPagination
应用
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
class MyPageNumberPagination(PageNumberPagination):page_size = 2page_query_param = 'page'# 定制传参page_size_query_param = 'size'# 最大一页的数据max_page_size = 5class MyLimitOffsetPagination(LimitOffsetPagination):default_limit = 2# 最大一页的数据max_limit = 5
class MyCursorPagination(CursorPagination):page_size=2ordering='-id'
from rest_framework.generics import ListAPIView
class AuthorListView(ListAPIView):serializer_class = serializers.AuthorModelSerializerqueryset = models.Author.objects.filter(is_delete=False)pagination_class =MyCursorPagination
四 异常处理 Exceptions
REST framework提供了异常处理,我们可以自定义异常处理函数。
4.1 使用方式
from rest_framework.views import exception_handlerdef custom_exception_handler(exc, context):# 先调用REST framework默认的异常处理方法获得标准错误响应对象response = exception_handler(exc, context)# 在此处补充自定义的异常处理if response is None:response.data['status_code'] = response.status_codereturn response
在配置文件中声明自定义的异常处理
REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
如果未声明,会采用默认的方式,如下
rest_frame/settings.py
REST_FRAMEWORK = {'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
4.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 DatabaseErrordef 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 校验失败
也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。
作者:liuqingzheng