12_07、过滤、排序、分页、异常
一、过滤
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中带清就能搜到
模型

from django.db import models class Book(models.Model): title = models.CharField(max_length=32) price = models.IntegerField() author = models.CharField(max_length=32)
序列化器

from app01 import models from rest_framework import serializers class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = '__all__'
路由

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)), ]
视图接口

from django.shortcuts import render from rest_framework.generics import ListAPIView, GenericAPIView # 九个视图子类 from app01.serializer import BookSerializer from app01.models import Book from rest_framework.viewsets import ViewSetMixin, GenericViewSet from rest_framework.mixins import ListModelMixin # 五个视图扩展类,配合GenericAPIView使用 from rest_framework.filters import SearchFilter # class BookView(ViewSetMixin, ListAPIView): # 查询所有接口(路由,九个子类之一) class BookView(GenericViewSet, ListModelMixin): # 查询所有接口(子路由,五个视图扩展类,需要配合GenericAPIView) queryset = Book.objects.all() # 获取所有 serializer_class = BookSerializer # 序列化 filter_backends = [SearchFilter, ] # search_fields = ['title'] # 按书籍名称模糊查询 search_fields = ['title', 'author'] # 按书籍名称、作者模糊查询
结果
2、第三方过滤器
思路
# 第三方过滤类(精准查询) ### 第0步:安装 pip3 install django-filter ### 第一步:注册 INSTALLED_APPS = [ 。。。 'django_filters', ] ### 第二步:导入过滤类 from django_filters.rest_framework import DjangoFilterBackend ### 第三步:在视图类中使用 class BookView(GenericViewSet,ListModelMixin): # 必须继承GenericAPIView,才有这个类属性 filter_backends = [DjangoFilterBackend,] # 需要配合一个类属性 filter_fields=['name','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=刘清政
注册
INSTALLED_APPS = [ 'django_filters' # 第三方过滤需要注册 ]
视图接口
from app01.serializer import BookSerializer from app01.models import Book from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin # 五个视图扩展类,配合GenericAPIView使用 from django_filters.rest_framework import DjangoFilterBackend class BookView(GenericViewSet, ListModelMixin): # 查询所有接口(子路由,五个视图扩展类,需要配合GenericAPIView) queryset = Book.objects.all() # 获取所有 serializer_class = BookSerializer # 序列化 filter_backends = [DjangoFilterBackend, ] # 第三方参数改变 filter_fields = ['title', 'author'] # 按书籍名称、作者模糊查询
结果
3、自定义过滤器
思路
# 自定义过滤类(模糊查询) ### 第一步:写一个类,继承BaseFilterBackend 基类,重写filter_queryset方法,返回qs对象,是过滤后的对象 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): #filter_backends = [BookNameFilter,] filter_backends = BookNameFilter ### 第三步:查询方式 http://127.0.0.1:8000/books/?name=红 # 模糊匹配 ,自己定义的
过滤器
from rest_framework.filters import BaseFilterBackend class BookNameFilter(BaseFilterBackend): # 写一个类,继承BaseFilterBackend # 重写filter_queryset方法,让返回的qs对象,是过滤后的对象 def filter_queryset(self, request, queryset, view): query = request.query_params.get('title') if query: queryset = queryset.filter(title__contains=query) return queryset
视图接口
from .filter import BookNameFilter class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() # 1,获取所有,赋值给queryset serializer_class = BookSerializer # 2,利用序列化器,json字段 # filter_backends = BookNameFilter # 不能这样写,type必须是一个可迭代对象 filter_backends = [BookNameFilter, ] # 3,利用过滤器
结果
自定义过滤器的源码解析:为什么这样写就能过滤,为什么这样写就能调用
# 源码分析----》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
过滤器与get请求参数的异同
二、排序
思路

# 按照id排序,按照年龄排序,按照价格排序 # 使用内置的即可 ## 第一步:导入内置排序类 from rest_framework.filters import OrderingFilter ## 第二步:在视图类中配置(必须继承GenericAPIView) class BookView(GenericViewSet,ListModelMixin): # 支持按price排序 filter_backends = [OrderingFilter,] ordering_fields=['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']
视图接口
from rest_framework.filters import OrderingFilter class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() # 1,获取所有,赋值给queryset serializer_class = BookSerializer # 2,利用序列化器,json字段 filter_backends = [OrderingFilter, ] # 3,实例化排序对象 Ordering_fields = ['price'] # 4,按价格排序
结果
三、分页
在五个接口中,注意分页的使用,只有在查询所有这个接口中,才有涉及
pc端,分页是上一页、下一页的展现形式
app和小程序中,分页是下拉,上拉的展现形式
这里提供三种分页方法
第一种:基本分页
思路

# 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&size=8
分页类
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条
视图类
from .page import CommonPageNumberPagination as PageNumberPagination class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() # 1,获取所有,赋值给queryset serializer_class = BookSerializer # 2,利用序列化器,json字段 pagination_class = PageNumberPagination
结果
第二种:偏移分页
思路

# LimitOffsetPagination---》偏移分页 #第一步:写一个类,继承LimitOffsetPagination,重写四个类属性 default_limit = 2 # 默认一页获取条数 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=1
分页类
# 偏移分页 from rest_framework.pagination import LimitOffsetPagination class CommonLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 默认一页获取条数 2 条 limit_query_param = 'limit' # ?limit=3 获取三条,如果不传,就用上面的默认两条 offset_query_param = 'offset' # ?limit=3&offset=2 从第2条开始,获取3条 ?offset=3:从第三条开始,获取2条 max_limit = 5 # 最大显示条数 5 条
视图类
from .page import CommonLimitOffsetPagination class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() # 1,获取所有,赋值给queryset serializer_class = BookSerializer # 2,利用序列化器,json字段 # pagination_class = PageNumberPagination pagination_class = CommonLimitOffsetPagination
结果
第三种:游标分页
思路

# CursorPagination---》游标分页 #第一步:写一个类,继承CursorPagination,重写四个类属性 class CommonCursorPagination(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
分页类
from rest_framework.pagination import CursorPagination class CommonCursorPagination(CursorPagination): page_size = 2 # 每页显示2条 cursor_query_param = 'cursor' # 查询条件 ?cursor=sdafdase ordering = 'id' # 排序规则,使用id排序
视图接口
from .page import CommonCursorPagination class BookView(GenericViewSet, ListModelMixin): queryset = Book.objects.all() # 1,获取所有,赋值给queryset serializer_class = BookSerializer # 2,利用序列化器,json字段 pagination_class = CommonCursorPagination
游标分页跟其他两种分页的区别
#### 注意: # 跟上面两种的区别:上面两种,可以从中间位置获取某一页,Cursor方式只能上一页和下一页 # 上面这两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据 # 下面这种方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据 # 这种分页方式特殊,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页 # 大数据量和app分页---》下拉加载下一页,不需要指定跳转到第几页
四、异常
思路

# 之前读APIViwe源码的时候,捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获 # 统一返回格式,无论是否异常,返回的格式统一 ,记录日志(好排查) {code:999,msg:服务器异常,请联系系统管理员} {code:100,msg:成功,data:[{},{}]} # 步骤: 第一步:写一个函数 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' # 再出异常,会执行这个函数 } ### 以后再出异常,都会走这个函数,后期需要记录日志,统一了返回格式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通