分页、排序、过滤功能、异常处理
分页、排序、过滤功能、异常处理
- 分页
- 排序
- 过滤
- 异常处理
分页
REST framework提供了分页的支持
添加分页的方式
1.可以在配置文件中设置全局的分页方式
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页数目
}
2.可以自定义分页类,为视图添加不同的分页方式,在视图中通过pagination_clas
属性来指明。
-
自定义分页类:
class LargeResultsSetPagination(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
如果在视图内关闭分页功能,只需在视图内设置pagination_class = None即可
分页器的类型
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 = 2 page_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 = StudentModelSerializer pagination_class = StandardPageNumberPagination # 127.0.0.1/four/students/?p=1&size=5
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
CursorPagination
-
前端访问的网址
使用GET请求 http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
-
子类中定义的属性
cursor_query_param:默认查询字段,不需要修改 page_size:每页数目 ordering:按什么排序,需要指定
排序
对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序
排序的要求
只有查询所有才设计到排序,其他的接口用不到
排序功能必须继承GenericAPIView+ListModelMixin的子视图类上
使用排序的步骤
-
配置排序类
filter_backends=[OrderingFilter,]
-
配置排序的字段
ordering_fields=['id','price']
-
支持前端的访问方式
http://127.0.0.1:8000/books/?ordering=-price,id
先按价格的降序排,如果价格一样再按id的升序排
排序的规则
排序是升序还是降序都是自己设置的,需要继承了APIView的,从请求地址中取出排序规则,自己排序
'price','-id'=reqeust.query_params.get('ordering').split(',')
qs = Book.objects.all().order_by('price','-id')
-
注意
分页和排序能一起用,但是是先排序后分页的
过滤
对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。
内置过滤
查询所有才涉及到过滤,其它接口都不需要
restful规范中有一条,请求地址中带过滤条件:分页,排序,过滤统称为过滤
内置过滤类使用步骤
必须是继承GenericAPIView+ListModelMixin的子视图类上
-
配置过滤类
filter_backends=[SearchFilter,]
-
配置过滤字段
ordering_fields=['name','publish']
-
支持前端的访问方式
http://127.0.0.1:8000/books/?search=过滤字段
只要name中或publish中有这个过滤的字段就都能搜出来
-
注意
内置过滤类只能通过search写条件,如果配置了多个过滤字段,是或者的条件
精准查询
内置的过滤类 只能通过?search=搜索条件 这种搜索,不能指定 name=三国&publish=出版社
-
精准查询步骤
1.需要导入第三方模块django-filter
pip3 install django-filter
2.配置在视图类中配置
filter_backends = [DjangoFilterBackend, ]
3.配置搜索的字段
filterset_fields = ['name', 'publish']
4.搜索
http://127.0.0.1:8008/books/?name=三国演义&publish=南京出版社
此搜索是精准搜索,条件是并且的关系,必须同时满足才能查询到
自定义过滤类
自定义过滤类步骤
-
写一个类,继承BaseFilterBackend
-
重写某个方法:filter_queryset,在该方法中实现过滤,返回qs对象
def filter_queryset(self, request, queryset, view): # 在这里面实现过滤,一定要返回qs对象,过滤后的数据 name = request.query_params.get('name', None) publish = request.query_params.get('publish', None) qs = queryset.filter(name__icontains=name, publish__icontains=publish) return qs
-
配置在视图类上
filter_backends = [MyFilter, ]
多个过滤类和排序类可以共用,filter_backends=[],可以配置多个,执行顺序是从做往右,所以,放在最左侧的尽量先过滤掉大部分数据
过滤类面试笑技巧
-
你在工作中遇到的问题及如何解决的?
我在写xx接口的时候,因为我们过滤条件很多,搜索数据库很慢,写了很多搜索类,之前时候搜索类的排序是随意的,搜索效率比较低,后来,读了drf搜索类的源码,发现执行顺序是从左往右,我就想到了,最左侧的搜索类,尽量搜索速度快,并且过滤掉的数据多,后续再搜索就会更快,所有我调整了一下搜索类的配置顺序,发现该接口的效率有所提升
全局异常处理
REST framework提供了异常处理,我们可以自定义异常处理函数。
内置异常处理
drf中无论在三大认证还是视图类的方法中执行,只要报错(主动抛异常),都能执行一个函数(异常处理的函数)
-
内置异常处理
from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先调用REST framework默认的异常处理方法获得标准错误响应对象 response = exception_handler(exc, context)
-
在配置文件中声明自定义的异常处理
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
自定义异常处理
自定义异常处理需要重写exception_handler函数
# 写一个函数
def common_exception_handler(exc, context):
# 记录日志:只要进入到这里面,一定是出了异常,只要出异常就要记录日志
request = context.get('request') # 当此请求的request
try:
username = request.user.username
except:
username = '没有登录'
ctime = time.time()
path = request.path
method_type = request.method
print('%s用户,在%s时间,访问%s接口,通过%s请求访问,出了错误:%s' % (username, str(ctime), path, method_type, str(exc)))
# 后期以上的代码要写到日志中
response = exception_handler(exc, context) # 内置的这个异常处理函数,只处理了drf自己的异常(继承了了APIException的异常)
if response: # 如果response有值,说明错误被处理了(Http404,PermissionDenied,APIException)
return Response({'code': 888, 'msg': 'drf错误,错误原因是:%s' % response.data.get('detail', '未知错误')})
else: # 如果是None,这个错误没有被处理,说明这个错误不是drf的错误,而是django的或代码的错误
return Response({'code': 999, 'msg': '系统错误,错误原因是:%s' % str(exc)})
settings配置文件中
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.exctptions.common_exception_handler'
}