首页 > 其他分享 >DRF之过滤,排序,自定义异常和分页

DRF之过滤,排序,自定义异常和分页

时间:2023-01-09 16:24:38浏览次数:39  
标签:自定义 title price queryset page 排序 class ### DRF

DRF 数据的过滤

  • 参考网址

https://www.cnblogs.com/songhaixing/p/14687072.html

  • drf 内置的过滤组件 SearchFilter
### models
class BookInfo(models.Model):
    title = models.CharField(max_length=64)
    simple_content = models.CharField(max_length=128)
    content = models.TextField()
    price = models.FloatField()

    def __str__(self):
        return self.title

### serializers
class BookInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = BookInfo
        fields = '__all__'

### views
from rest_framework.filters import SearchFilter

class BookInfoListView(ListAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_backends = [SearchFilter] # 指定过滤类
    # 这里不能写成 filter_fields
    search_fields = ('price','title') # 指定字段

### 测试
# 127.0.0.1:8000/book2/?search=12
# 127.0.0.1:8000/book2/?search=2  (模糊查询)
# 127.0.0.1:8000/book2/?title=雪  (模糊查询,无效)
  • 小结
- 支持模糊查询
- 只能使用 search 来进行条件指定
- 可以使用 "," 来隔离多个条件来进行查询
- 不能查询外键字段,报错 : Related Field got invalid lookup: icontains
- DRF内置的过滤查询,局限性较大

第三方过滤组件 Django-filter

  • 安装一个坑
- 不要直接 pip install django-filter,会把django更新成最新版

    - 在pycharm中安装即可(不要安装最新版,最新版有坑,没有效果)
    - 目前安装的是 2.4.0(22.1是最新版,很坑,不要去装...)
### settings
INSTALLED_APPS = [
    ...
    'django_filters',
]

# 全局配置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}

### views 局部配置
from django_filters.rest_framework import DjangoFilterBackend

class BookInfoListView(ListAPIView):
  
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_backends = [DjangoFilterBackend,] # 指定过滤类
    filter_fields = ('price','title') # 指定字段

### 测试
# 127.0.0.1:8000/book2/?title=萨嘎达
# 127.0.0.1:8000/book2/?price=2  (不支持模糊查询)
# 127.0.0.1:8000/book2/?price=34&title=阿嘎达

  • 小结
- 可以指定字段名进行查询 (还有一些其他强大功能,如:通过时间过滤,(后边介绍))
- 不支持模糊查询
- 针对django内置搜索组件的拓展, 在django内置的基础之上还拓展了外键字段的过滤功能

自定义过滤查询(推荐)

- 创建一个存放过滤类的文件: custom_filter.py

- 继承 BaseFilterBackend , 必须重写 filter_queryset 方法
### custom_filter.py
from rest_framework.filters import BaseFilterBackend
from django.db.models import F,Q

# 自定义过滤类(继承BaseFilterBackend)
class MyFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        price = request.GET.get('price')
        title = request.GET.get('title')
        # 匹配的逻辑代码自己定制
        if title and price:
            # contains : 包含(模糊匹配)
            queryset = queryset.filter(Q(title__contains=title)|Q(price__contains=price))  
        if title:
            queryset = queryset.filter(title__contains=title)
        if price:
            queryset = queryset.filter(price__contains=price)
        return queryset

### views
from .custom_filter import MyFilter

class BookInfoListView(ListAPIView):
   
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_backends = [MyFilter,] # 指定自定义过滤类

### 测试
# 127.0.0.1:8000/book2/?title=萨嘎达
# 127.0.0.1:8000/book2/?price=2  (模糊查询)
# 127.0.0.1:8000/book2/?price=2&title=雪  (多个条件模糊查询)

  • 小结
- 可以自定义匹配逻辑
- 自定义模糊匹配等等, 扩展性比较强

DRF 排序

### settings
......
REST_FRAMEWORK = {
        # 过滤
        # 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
        # 排序(key都是一样的)
        # 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.OrderingFilter'),
        # 过滤和排序放一起
        'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter'),
        }

### views
from rest_framework.filters import OrderingFilter

class BookInfoListView(ListAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    filter_backends = [MyFilter,OrderingFilter] # 指定排序类
    ordering_fields = "__all__" # 排序所有的字段

### 测试
# 127.0.0.1:8000/book2/?ordering=price   # 默认升序
# 127.0.0.1:8000/book2/?ordering=-price  # - 号,反序
# 127.0.0.1:8000/book2/?ordering=-price,title  # 若第一个条件重复可以继续使用第二个条件
# 127.0.0.1:8000/book2/?ordering=-price,-title

DRF 自定义异常

  • 在原有异常的基础上,可以加上自定义的异常

  • 参考网址

https://www.cnblogs.com/songhaixing/p/14687072.html

### 美多商城示例: 在原有异常的基础上,添加了 数据库异常处理
# utils.exception
from rest_framework.views import exception_handler as drf_exception_handler
import logging
from django.db import DatabaseError
from redis.exceptions import RedisError
from rest_framework.response import Response
from rest_framework import status

# 获取在配置文件中定义的logger,用来记录日志
logger = logging.getLogger('django')

def exception_handler(exc, context):
    """
    自定义异常处理
    :param exc: 异常
    :param context: 抛出异常的上下文
    :return: Response响应对象
    """
    # 调用drf框架原生的异常处理方法
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view']
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            # 数据库异常
            logger.error('[%s] %s' % (view, exc))
            response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

分页介绍

  • DRF 框架提供了三个类来实现分页功能
- PageNumberPagination : 可选分页类, 可以选择查询某一页内容
    # 子类中可定义的属性 : 
    - page_size              # 每页数目
    - page_query_param       # 前端发送的页数关键字名,默认为”page”
    - page_size_query_param  # 前端发送的每页数目关键字名,默认为None
    - max_page_size          # 前端最多能设置的每页数量

- LimitOffsetPagination : 偏移分页类, 可以指定从第几条数据开始查询
    # 子类中可定义的属性
    - default_limit             # 默认限制,默认值与PAGE_SIZE设置一直
    - limit_query_param limit   # 参数名,默认’limit’
    - offset_query_param offset # 参数名,默认’offset’
    - max_limit                 # 最大limit限制,默认None

- CursorPagination : 游标分页类(加密分页), 只能上一页下一页, 不能指定, 并且url中的页码是加密的
    # 子类中可定义的属性
    - cursor_query_param  # 默认查询字段
    - page_size           # 每页数目
    - ordering            # 按什么排序,需要指定
  • 可选分页类的使用 : PageNumberPagination

    • 直接使用类实例化来实现
class PageNumberView(APIView):

    authentication_classes = []
    permission_classes = []

    def get(self, request, *args, **kwargs):
        # 拿到所有数据
        book_obj = BookInfo.objects.all()
        # 使用类实例化得到分页器对象
        page_obj = PageNumberPagination()
        # 设置四个参数
        page_obj.page_size = 2                   # 默认每页显示的条数
        page_obj.max_page_size = 4               # 每页最大显示条数
        page_obj.page_query_param = 'page'       # 查询页数的关键字(?page=2)
        page_obj.page_size_query_param = 'size'  # 每页显示查询条数条件关键字(?page=2&size=2)
        # 调用分页器对象方法对数据进行分页处理
        # 参数: queryset(数据集),request(请求),view(处理分页的视图类)
        page = page_obj.paginate_queryset(queryset=book_obj, request=request, view=PageNumberView)
        # 将分页后的数据对象进行序列化处理
        page_ser = BookInfoSerializer(instance=page, many=True)
        # get_next_link() : 下一页
        # get_previous_link() : 上一页
        return Response({'status': 200, 'msg': '查询成功', 'data': page_ser.data, 'next': page_obj.get_next_link(),'previous': page_obj.get_previous_link()})
        # 或者直接使用分页类自带的Response(自带上一页下一页)
        # return page_obj.get_paginated_response(page_ser.data)

### 测试
# 跳到第1页,每页显示3条数据
http://127.0.0.1:8888/tests/books/page?page=1&size=3
  • 注意事项: 超过显示的页数, 则显示 "无效页面"

  • 自定义普通(页码)分页类: 和上述一模一样的效果(推荐)

### pagination
from rest_framework.pagination import PageNumberPagination

class CustomPageNum(PageNumberPagination):
    page_size = 3
    max_page_size = 4
    page_query_param = 'page'
    page_size_query_param = 'size'

### views
class PageNumberView(ListAPIView):

   
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = CustomPageNum # 导入自定义的分页类

偏移分页类的使用 : LimitOffsetPagination

  • 直接使用类实例化来实现(逻辑与上述类似)

  • 自定义偏移分页类

### pagination
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination

class CustomPageNum(PageNumberPagination):
    ......


class CustomLimitOff(LimitOffsetPagination):
    default_limit = 2
    max_limit = 5
    limit_query_param = 'limit'
    offset_query_param = 'offset'

### views

class PageNumberView(ListAPIView):

    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = CustomLimitOff # 指定分页类

### 测试: http://127.0.0.1:8888/tests/books/page?limit=1&offset=2
# 显示第三条数据

游标(加密)分页类的使用 : CursorPagination

  • 直接使用类实例化来实现(不再赘述)

  • 自定义游标类实现: 只能上一页和下一页

### pagination
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class CustomPageNum(PageNumberPagination):
    ......


class CustomLimitOff(LimitOffsetPagination):
    ......

class CustomCursor(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 2
    ordering = 'id' # 排序

### views
class PageNumberView(ListAPIView):

    authentication_classes = []
    permission_classes = []
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = CustomCursor

### 测试: http://127.0.0.1:8888/tests/books/page?cursor=cD0y
  • 报错问题: Using cursor pagination, but filter class OrderingFilter returned a None ordering.
# 过滤以及排序与加密分页的冲突,如果setting.py设置了自定义的排序就会出现该问题
# 在setting.py文件中将'DEFAULT_FILTER_BACKENDS'注释

三种分页类总结

- CursorPagination也可以被称为加密分页, 会对页码进行加密处理, 访问者无法通过修改页码来进行访问
- 这种方式相对于PageNumberPagination分页的优点是避免因用户任意修改页码, 从而数据库查询数量过大, 造成数据库过载和查询速度慢的问题
- 这个也是数据库查询性能优化, 例如PageNumberPagination中用户可以直接将页码改为10000, 数据库需要从头遍历检索到10000这条记录
- 而CursorPagination中只能查看上下页, 对数据库产生的压力极小, 但对用户的体验不友好

标签:自定义,title,price,queryset,page,排序,class,###,DRF
From: https://www.cnblogs.com/qinganning/p/17037362.html

相关文章

  • 实现自定义 Spring AOP 注解
    实现自定义SpringAOP注解翻译原文链接ImplementingaCustomSpringAOPAnnotation1.介绍在本文中,我们将使用Spring中的AOP支持来实现自定义AOP注解。Int......
  • 后缀数组 I —— 后缀排序
    后缀数组(suffixarray)是省选字符串题目中非常重要的算法。本文将简略讲述其\(O(n\logn)\)求法,对于时间复杂度更优秀但notpractical的做法不作提及。模板考虑一种......
  • NoneBot2聊天机器人自定义聊天内容
    1、按照上一篇文章所介绍的方法新建一个机器人,进入所对应的文件夹,会发现里面有一个和自己所创建的机器人名称一样的文件夹   2、进入该文件夹,会发现一个名叫plugins......
  • 当你自定义一个删除的delete方法时,不加事务注解,报错
    报错:cannotreliablyprocess'remove'call要用的注解:@Modifying@TransactionalintdeleteUserByUserName(Stringusername); 参考:jpa报错:NoEntityM......
  • 4655. 重新排序
    4655.重新排序给定一个数组A和一些查询Li,Ri,求数组中第Li至第Ri个元素之和。小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能......
  • 数组排序
    /***数组元素交换位置*@param{array}arr数组*@param{number}index1添加项目的位置*@param{number}index2删除项目的位置*index1和index2分别是两个数组......
  • 关于快速排序算法最多比较次数与最少比较次数的问题
    关于快速排序算法最多比较次数与最少比较次数的问题最常见的快速排序算法的衡量标准是时间复杂度,即最坏情况\(O(n)\),最优与平均情况均为\(O(n\log_2^n)\)。最近看到......
  • 选择&冒泡&插入排序以及交换两数的三种方式
    选择排序//0~n位先排第0位的,将1~n的分别与0上的比较,如果小于它,交换//再排第1位,将2~n的分别与0上的比较,如果小于它,交换//以此类推publicstaticvoidselectSo......
  • 39、商品服务--品牌管理--JSR303自定义校验注解
    假若SpringMvc提供的校验注解不能满足我们的要求,我们就自己写一个1、编写一个自定义校验注解(即编写一个注解类--Annotation类)参考其他的注解,来编写我们自己的注解我......
  • 图文并茂strapi 4.5.5自定义搭建指南以及数据库字段名接口返回mapping分析
    strapi是什么?基于Nodejs的开源免费CMS框架为什么选择它?基于nodejs,100%JavaScript,上手迅速可轻松创建功能强大且可自定义的API可以使用任何喜欢的数据库先决条件首......