06-排序 分页 过滤
排序
查询多条和全部才会用到排序
- 排序关键字:ordering
查询字符串
查询字符串(Query String)是指在 URL 中以问号(?)开始的部分,用于向服务器传递参数。它由一个或多个键值对组成,每个键值对之间用 &
符号分隔。
例如,在以下 URL 中,查询字符串是 ?page=2&category=books
:
在django种如何使用排序 ?ordering=price
-- 按价格升序
http://127.0.0.1:8000/books/?ordering=price
select * from dd_book_spider dbs order by price asc;
-- 按价格降序 字段面前写一个-号
http://127.0.0.1:8000/books/?ordering=-price
select * from dd_book_spider dbs order by price desc;
-- 多个排序,用英文逗号隔开即可,一样支持 - 号从大到小排序
http://127.0.0.1:8000/books/?ordering=publish,-price
-- 出版社降序,价格也降序
http://127.0.0.1:8000/v1/publish/?ordering=-publish,-price
如何使用 必须是继承 GenericAPIView 的视图类才能使用
# 导入模块
from rest_framework.filters import OrderingFilter
class PublishView(GenericViewSet, ListModelMixin):
# 固定写法
filter_backends = [OrderingFilter]
# 按哪一个字段取过滤,记不住取源码查看
ordering_fields = ['price', "name", "publish"]
# 源码 serializer_class
def get_default_valid_fields(self, queryset, view, context={}):
# ...
serializer_class = getattr(view, 'serializer_class', None)
定制指定格式
如果纯自己写,不生效。
如果按照下面的格式,生效。
只要调用了self.get_queryset() 就会实现过滤
class PublishView(GenericViewSet, ListModelMixin):
# ...
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()) # 主要是这句话
serializer = BookSerializer(instance=queryset, many=True)
return Response({"code": 100, "msg": "成功", "results": serializer.data})
过滤
drf的过滤 (模糊匹配)
按照指定的条件取过滤
如何使用 ?searsh=publish
# 导入模块
from rest_framework.filters import SearchFilter
class PublishView(GenericViewSet, ListModelMixin):
# 固定写法
filter_backends = [SearchFilter]
# 按哪一个字段取过滤,记不住取源码查看
search_fields = ['price', "name", "publish"]
过滤和排序支持一起使用 ?serach=publish&ordering=price
from rest_framework.filters import OrderingFilter, SearchFilter
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ["name"]
ordering_fields = ["price"]
# 按纪念排序
http://127.0.0.1:8000/v1/publish/?search=纪念
# 按纪念排序然后按价格降序
http://127.0.0.1:8000/v1/publish/?search=纪念&ordering=-price
# ================= 控制字段进行模糊匹配
ordering_fields = ["publish", "name"]
# 只要书名或者出版社带 北 都能匹配
http://127.0.0.1:8000/v1/publish/?search=北
第三方的过滤(支持精准匹配)django-filter
如何使用 直接使用查询字符串
# 安装模块
pip install django-filter
# 导入模块
from django_filters.rest_framework import DjangoFilterBackend
# 使用(一样需要继承GenericAPIView视图类 )
class PublishView(GenericViewSet, ListModelMixin):
queryset = DdBookSpider.objects.all()
serializer_class = BookSpiderSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ["price", "publish", "name"]
# 查询出版社为 九州出版社 并且价格为36的结果
http://127.0.0.1:8000/v1/publish/?publish=九州出版社&price=36
更多django-filter的用法:https://www.cnblogs.com/juelian/p/17560269.html
自定义过滤器(实现更高级的功能)
下面的这个写法就是
from rest_framework.filters import BaseFilterBackend
from django.db.models import Q
class CommonFilter(BaseFilterBackend):
"""
request:表示客户端发送的 HTTP 请求,从而根据请求进行过滤操作。
queryset:表示要进行过滤的查询集,通常是从数据库中获取的数据集合。
view: 表示处理请求的视图类实例,即使用了自定义过滤器的视图类。
通过 view 参数,可以访问视图类中定义的属性、方法等信息,以便根据视图的特定需求进行过滤操作。
"""
def filter_queryset(self, request, queryset, view):
name = request.query_params.get("name", None)
price = request.query_params.get("price", None) # --> 等同于 request.get("price")
publish = request.query_params.get("publish", None)
# queryset --> 视图层的 我这里是DdBookSpider.objects.all()
# queryset 是可以一直 ... 查询的
if name and price and publish:
queryset = queryset.filter(Q(price=price) | Q(publish__contains=publish))
if name:
queryset = queryset.filter(name__contains=name)
if price:
queryset = queryset.filter(price=price)
if publish:
queryset = queryset.filter(publish__contains=publish)
return queryset
# 视图层
from .filter import CommonFilter
class PublishView(GenericViewSet, ListModelMixin):
queryset = DdBookSpider.objects.all()
serializer_class = BookSpiderSerializer
# 写这个就可以了,不用写字段了
filter_backends = [CommonFilter]
过滤和排序总结
- 必须继承 GenericAPIView
- 不论是自己写的过滤器,还是第三方的过滤器,都写在 filter_backends列表即可,会从左往右依次筛选
- 一般是过滤器+排序组合,而不是多个过滤器组合,因为每一个匹配规则都是不一样的
- 自定义的过滤器只需要写filter_backends就可以了,逻辑是我们后端自己取控制的
分页 (不建议重写list)
如果数据库的数据量很大,如果一次性把数据返回给前端肯定体验不好,也很消耗内存占用,这时候就使用到了分页。
- 使用了这个分页后,就可以放弃django默认的分页器了。
- 分页和过滤 排序 不冲突
分页的展现形式:
- web端:有页数
- 小程序等:上滑页面
from rest_framework.pagination import CursorPagination, PageNumberPagination, LimitOffsetPagination
方式1 基本分页 PageNumberPagination (推荐)
# 导入模块
from rest_framework.pagination import PageNumberPagination
# 写一个类 继承它
class CommonPagination(PageNumberPagination):
# 定制几个类属性就可以了
# n 每一页显示多少条,这里是10条
page_size = 10
# ?后面的查询参数 可以修改 前端指定数量会然后跳转到指定的页数
# http://127.0.0.1:8000/v1/publish/?page=10
page_query_param = 'page'
# 每页最多显示几条
# 比如下面 查询第10页每一页显示5条
# http://127.0.0.1:8000/v1/publish/?page=10&size=5
page_size_query_param = "size"
# 控制size参数的最多条数 超过也是按照这个为准 比如定成了10 size为100 那么还是按10来
max_page_size = 10
# http://127.0.0.1:8000/v1/publish/?page=2&size=5 查询第2页 每页显示5条
# http://127.0.0.1:8000/v1/publish/?page=6&size=599 查询第6页 每页显示10条
# 视图层
from .pagination import CommonPagination
class PublishView(GenericViewSet, ListModelMixin):
queryset = DdBookSpider.objects.all()
serializer_class = BookSpiderSerializer
pagination_class = CommonPagination
方式2 LimitOffsetPagination
from rest_framework.pagination import LimitOffsetPagination
class CommonLimitOffsetPagination(LimitOffsetPagination):
# 每页显示几条
default_limit = 5
# 控制每页显示多少条
# http://127.0.0.1:8000/v1/publish/?limit=5
limit_query_param = 'limit'
# 偏移量
# http://127.0.0.1:8000/v1/publish/?offset=3 从第三条开始 取5条 (default_limit)
# http://127.0.0.1:8000/v1/publish/?offset=4&limit=3 从第4条开始取3条 写了limit受limit控制
offset_query_param = 'offset'
# 每页最大的条数 写500也是10条
max_limit = 10
# 视图层
from .pagination import CommonLimitOffsetPagination
# 限制超级管理员才可以访问
class PublishView(GenericViewSet, ListModelMixin):
queryset = DdBookSpider.objects.all()
serializer_class = BookSpiderSerializer
pagination_class = CommonLimitOffsetPagination
方式3 CursorPagination
- 必须先排序
- 页数不确定以及很多使用
# 分页
from rest_framework.pagination import CursorPagination
# 必须先排序 只能上一页或者下一页 不能直接跳转到某一页
# 在app端上使用的比较多,效率最高,数据量越大越明显 比如几十万条
class CommonCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询条件 同上面的 limit page
page_size = 10 # 每一页显示的条数
ordering = 'id' # 按照什么去排序