过滤、排序、分页、异常处理
2022/4/7 6:23:01
本文主要是介绍过滤、排序、分页、异常处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
上期内容回顾
# 继承一个父类,父类中有方法,在子类中重写方法 # 鸭子类型: 不需要显示继承一个类,只要多个类中有同样的属性或方法,我们把它们称之为一种类,python,go # 非鸭子类类型语言: 如果要属于同一类,必须显示的继承某个基类,这样才属于基类这个类型,java # python语言建议使用鸭子类型(约定),但在实际开发中,我们经常不使用鸭子类型这种特性,出错概率低 # 实际编码: 要么认为约定必须有哪些方法(符合鸭子类型),可控性低; 要么强制约定有哪些方法(abc模块,使用抛异常) # java中: 重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变 # java中: 重载:是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同 # 认证 写一个类,继承BaseAuthentication,重写authenticate,在方法中校验,如果登录了,返回两个值,如果没登陆,抛出异常 全局使用 局部使用 局部禁用 # 权限 写一个类,继承BasePermission,重写has_permission方法,在方法中判断,如果有权限,返回True,如果没有权限,返回false 全局使用 局部使用 局部禁用 # 频率 写一个类,继承SimpleRateThrottle 重写get_cache_key,返回什么就以什么做频率限制 重写类属性scope = 'minute_3',在配置文件中配置:'DEFAULT_THROTTLE_RATES': {'minute_3': '3/m'} 全局使用 局部使用 局部禁用
今日内容概要
- 过滤
- 排序
- 分页
- 异常处理
内容详细
1、过滤
# 5个接口中,只有获取所有 需要过滤,其他都不需要 # 过滤有多种: 内置的过滤类 第三方过滤类 自定义过滤类 # 1、内置的过滤类 ### 第一步:导入 from rest_framework.filters import SearchFilter ### 第二步:在视图类中写 # 在视图类中 # 必须继承GenericAPIView,才有这个类属性 filter_backends = [SearchFilter, ] # 需要配合一个类属性,可以按name过滤 search_fields = ['name', 'author'] ### 第三步:搜索的时候,模糊搜索 http://127.0.0.1:8000/books/?search=火 http://127.0.0.1:8000/books/?search=田 # 书名或者author中带田就能搜到 # 2、第三方过滤类 ###第一步:安装模块 pip3 install django-filter ### 第二步:注册 INSTALLED_APPS = [ 'django_filters', ] ### 第三步:导入过滤类 from django_filters.rest_framework import DjangoFilterBackend ### 第四步:在视图类中使用 # django_filters和rest_framework都需要去配置文件中注册 class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer # 必须继承GenericAPIView,才有这个类属性 filter_backends = [DjangoFilterBackend, ] # 需要配合一个类属性, filter_fields = ['name', 'author'] # 书名或者author中同时过滤 ### 第五步:查询方式 http://127.0.0.1:8000/books/?name=火影忍者 http://127.0.0.1:8000/books/?name=海贼王&author=尾田 # and条件 http://127.0.0.1:8000/books/?author=尾田 # 3、自定义过滤类 ### 第一步:写一个类,继承BaseFilterBackend 基类,重写filter_queryset方法, 返回qs对象,是过滤后的对象 from rest_framework.filters import BaseFilterBackend class BookNameFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): query = request.query_params.get('name') if query: queryset = queryset.filter(name__contains=query) return queryset ### 第二步:在视图类中使用 from .filter import BookNameFilter class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer # 必须继承GenericAPIView,才有这个类属性 filter_backends = [BookNameFilter, ] # 自定义的过滤类完成了过滤,不需要写类属性了 ### 第三步:查询方式 http://127.0.0.1:8000/books/?name=忍 # 模糊查询 也可自己定义查询条件 """ ### 源码分析: GenericAPIView----》查询所有,调用了list---》self.filter_queryset(self.get_queryset())----》查看GenericAPIView的filter_queryset方法: def filter_queryset(self, queryset): for backend in list(self.filter_backends): queryset = backend().filter_queryset(self.request, queryset, self) return queryset """
数据准备:models.py:
# 创建一个Book表 添加两条测试数据 迁移数据库 from django.db import models class Book(models.Model): name = models.CharField(max_length=32) price = models.IntegerField() author = models.CharField(max_length=32)
新建:serializer.py:
from .models import Book from rest_framework import serializers class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = '__all__'
新建 filter.py:
from rest_framework.filters import BaseFilterBackend class BookNameFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): query = request.query_params.get('name') if query: queryset = queryset.filter(name__contains=query) return queryset
视图类 views.py:
from .models import Book from .serializer import BookSerializer from rest_framework.generics import ListAPIView from rest_framework.viewsets import ViewSetMixin, GenericViewSet from rest_framework.filters import SearchFilter from rest_framework.mixins import ListModelMixin # 过滤: ### 第一种:使用内置过滤类 # class BookView(ViewSetMixin, ListAPIView): # class BookView(GenericViewSet, ListModelMixin): # 同上 必须继承自动生成路由的类 # queryset = Book.objects.all() # serializer_class = BookSerializer # # # 必须继承GenericAPIView,才有这个类属性 # filter_backends = [SearchFilter, ] # # # 需要配合一个类属性, # search_fields = ['name', 'author'] # 书名或者author中同时过滤 ### 第二种:使用第三过滤类 from django_filters.rest_framework import DjangoFilterBackend # django_filters和rest_framework都需要去配置文件中注册 # class BookView(GenericViewSet, ListModelMixin): # queryset = Book.objects.all() # serializer_class = BookSerializer # # # 必须继承GenericAPIView,才有这个类属性 # filter_backends = [DjangoFilterBackend, ] # # # 需要配合一个类属性, # filter_fields = ['name', 'author'] # 书名或者author中同时过滤 ### 第三种:使用自定义过滤类 from .filter import BookNameFilter class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer # 必须继承GenericAPIView,才有这个类属性 filter_backends = [BookNameFilter, ] # 自定义的过滤类完成了过滤,不需要写类属性了
路由修改 urls.py:
from django.contrib import admin from django.urls import path, include from rest_framework.routers import SimpleRouter from app01 import views router = SimpleRouter() router.register('books', views.BookView, 'books') urlpatterns = [ path('admin/', admin.site.urls), path('', include(router.urls)), ]
2、排序
# 按照id排序,按照年龄排序,按照价格排序... # 使用内置的即可 ### 第一步:导入内置排序类 from rest_framework.filters import OrderingFilter ### 第二步:在视图类中配置(必须继承GenericAPIView) class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer filter_backends = [OrderingFilter, ] ordering_fields = ['price', 'id'] # 先按照price排序 如果price一样 那么按id继续排序 ### 第三步:查询 http://127.0.0.1:8000/books/?ordering=price # 按price正序排 http://127.0.0.1:8000/books/?ordering=-price # 倒序排 http://127.0.0.1:8000/books/?ordering=-price,-id # 先按价格倒序排,如果价格一样,再按id倒序排 """ ### 过滤和排序可以同时用: 因为他们本质是for循环一个个执行,所有优先使用过滤,再使用排序 filter_backends = [SearchFilter, OrderingFilter] ordering_fields=['price', 'id'] search_fields=['name', 'author'] """
3、分页
# 5个接口中,只有查询所有,涉及到分页 # pc端是下一个页,下一页的形式,如果在app,小程序中展现形式是下拉加载下一个 # 默认提供了,三种分页方式: PageNumberPagination LimitOffsetPagination CursorPagination # 1、PageNumberPagination---基本分页 按照页码数,每页显示多少条 ### 第一步:写一个类,继承PageNumberPagination,重写四个类属性 page_size = 3 # 每页显示条数,默认 page_query_param = 'page' # 查询条件叫page --> ?page=3 page_size_query_param = 'size' # 每页显示的条数的查询条件: ?page=3&size=9 查询第三页,第三页显示9条 max_page_size = 5 # 每页最大显示多少条:?page=3&size=9,最终还是显示5条 ### 第二步:配置在视图类上,必须继承GenericAPIView才有这个类属性 pagination_class = PageNumberPagination # 第三步:查询方式 http://127.0.0.1:8000/books/?page=2 # 查询第二页 每页显示默认的三条 http://127.0.0.1:8000/books/?page=2&size=4 # 查询第二页 每页显示四条 # 2、LimitOffsetPagination---偏移分页 ### 第一步:写一个类,继承LimitOffsetPagination,重写四个类属性 default_limit = 2 # 默认一页获取条数 limit_query_param = 'limit' # ?limit=3 获取三条,如果不传,就用上面的默认两条 offset_query_param = 'offset' # ?limit=3&offset=2 从第2条开始,获取3条 ?offset=3:从第三条开始,获取默认2条 max_limit = 5 # 最大显示条数 5 条 ### 第二步:配置在视图类上,必须继承GenericAPIView才有这个类属性 pagination_class = CommonLimitOffsetPagination ### 第三步:查询方式 http://127.0.0.1:8000/books/?limit=2&offset=3 # 从第三条开始获取两条 # 3、CursorPagination---游标分页 ### 第一步:写一个类,继承CursorPagination,重写四个类属性 page_size = 2 # 每页显示2条 cursor_query_param = 'cursor' # 查询条件 ?cursor=sdafdase ordering = 'id' # 排序规则,使用id排序 ### 第二步:配置在视图类上,必须继承GenericAPIView才有这个类属性 pagination_class = CommonCursorPagination ### 第三步:查询方式 http://127.0.0.1:8000/books/?cursor=cD02
新建 page.py:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class CommonPageNumberPagination(PageNumberPagination): # 重写四个类属性 page_size = 3 # 每页显示条数,默认 page_query_param = 'page' # 查询条件叫page --> ?page=3 page_size_query_param = 'size' # 每页显示的条数的查询条件: ?page=3&size=9 查询第三页,第三页显示9条 max_page_size = 5 # 每页最大显示多少条:?page=3&size=9,最终还是显示5条 class CommonLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 默认一页获取条数 limit_query_param = 'limit' # ?limit=3 获取三条,如果不传,就用上面的默认两条 offset_query_param = 'offset' # ?limit=3&offset=2 从第2条开始,获取3条 ?offset=3:从第三条开始,获取默认2条 max_limit = 5 # 最大显示条数 5 条 class CommonCursorPagination(CursorPagination): page_size = 2 # 每页显示2条 cursor_query_param = 'cursor' # 查询条件 ?cursor=sdafdase ordering = 'id' # 排序规则,使用id排序
视图类 views.py中:
""" 分页 """ # 分页种类一:PageNumberPagination 基本分页 用的最多 from .page import CommonPageNumberPagination as PageNumberPagination from .page import CommonLimitOffsetPagination, CommonCursorPagination # 分页一 二: # class BookView(GenericViewSet, ListModelMixin): # queryset = Book.objects.all() # serializer_class = BookSerializer # # filter_backends = [OrderingFilter, SearchFilter] # ordering_fields = ['price', 'id'] # 排序 # search_fields = ['name', 'author'] # 过滤 # 不要放在列表中了,分页只能按一种方式,不能按多种方式 # pagination_class = PageNumberPagination # pagination_class = CommonLimitOffsetPagination # 分页三: """ 跟上面两种的区别:上面两种,可以从中间位置获取某一页,Cursor方式只能上一页和下一页 上面这两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据 下面这种方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据 这种分页方式特殊,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页 适用于:大数据量和app分页---》下拉加载下一页,不需要指定跳转到第几页 """ class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer filter_backends = [SearchFilter] search_fields = ['name', 'author'] # 过滤 pagination_class = CommonCursorPagination # 验证此类分页时 视图类中不可以带有排序功能 要注释掉
4、异常处理
# 之前读APIViwe源码的时候,捕获了全局异常 在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获 # 统一返回格式,无论是否异常,返回的格式统一 ,记录日志(好排查) {code:999,msg:服务器异常,请联系系统管理员} {code:100,msg:成功,data:[{},{}]} ### 第一步:写一个函数 新建exception.py文件: from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它 from rest_framework.response import Response def common_exception_handler(exc, context): # 第一步,先执行原来的exception_handler # 第一种情况,返回Response对象,这表示已经处理了异常,它只处理APIExcepiton的异常,第二种情况,返回None,表示没有处理 res = exception_handler(exc, context) if res: # exception_handler 已经处理了,暂时先不处理了 # 998:APIExcepiton # res=Response(data={'code':998,'msg':'服务器异常,请联系系统管理员'}) res = Response(data={'code': 998, 'msg': res.data.get('detail', '服务器异常,请联系系统管理员')}) else: # 999:出了APIExcepiton外的异常 # res=Response(data={'code':999,'msg':'服务器异常,请联系系统管理员'}) res = Response(data={'code': 999, 'msg': str(exc)}) # 注意:咱们在这里,可以记录日志---》只要走到这,说明程序报错了,记录日志,以后查日志---》尽量详细 # 出错时间,错误原因,哪个视图类出了错,什么请求地址,什么请求方式出了错 request = context.get('request') # 这个request是当次请求的request对象 view = context.get('view') # 这个viewt是当次执行的视图类对象 print('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method)) return res ### 第二步:把函数配置在配置文件中: REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'app01.exception.common_exception_handler' # 再出异常,会执行这个函数 }
视图类 views.py:
### 全局异常,继承APIView及其子类就可以 from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.exceptions import APIException class TestView(APIView): def get(self, request): ## 第一:程序出错 # l=[1,2,3] # print(l[99]) ## 第二:主动抛异常 raise Exception('我错了') # 第三:主动抛 APIException的异常 # raise APIException('APIException我错了') return Response('ok')
配置测试路由 urls.py:
path('test/', views.TestView.as_view()),
这篇关于过滤、排序、分页、异常处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-13TiDB + ES:转转业财系统亿级数据存储优化实践
- 2024-05-09“2024鸿蒙零基础快速实战-仿抖音App开发(ArkTS版)”实战课程已上线
- 2024-05-09聊聊如何通过arthas-tunnel-server来远程管理所有需要arthas监控的应用
- 2024-05-09log4j2这么配就对了
- 2024-05-09nginx修改Content-Type
- 2024-05-09Redis多数据源,看这篇就够了
- 2024-05-09Google Chrome驱动程序 124.0.6367.62(正式版本)去哪下载?
- 2024-05-09有没有大佬知道这种数据应该怎么抓取呀?
- 2024-05-09这种运行结果里的10.100000001,怎么能最快改成10.1?
- 2024-05-09企业src漏洞挖掘-有意思的命令执行