DRF过滤排序分页及异常处理

目录

分页

分页源码

排序

过滤

过滤类的其他使用

过滤排序源码分析

异常处理

异常捕获源码分析


分页,过滤,排序是针对数据进行的操作,主要是指为了减少数据库查询的压力。

分页

1. 定义一个分页类,继承drf提供的三个分页类,重写其中的属性

2. 视图函数必须继承GenericAPIView才能使用分页, 在视图类中加一个pagination_class属性,属性值是自定义的分页类, 一般只有查询所有数据的需求时才会用到分页

pagination.py

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination


class PageNumber(PageNumberPagination):
    page_size = 3  # 每页展示数据个数
    page_query_param = 'page'  # 前端发送的页数关键字名,默认为”page”
    page_size_query_param = 'size'  # 前端发送的每页数目关键字名,默认为None
    max_page_size = 10  # 前端最多能设置的每页数量


class LimitOffset(LimitOffsetPagination):
    default_limit = 3  # 每页展示数据个数
    limit_query_param = 'limit'  # 前端发送的页数关键字名,默认为”limit”
    offset_query_param = 'offset'  # 偏移值
    max_limit = 5  # 最大limit限制,默认None


class Cursor(CursorPagination):
    cursor_query_param = 'cursor'  # 默认查询关键字
    page_size = 3  # 每页展示数据个数
    ordering = 'id'  # 按什么排序,需要指定

views.py

from .pagination import PageNumber, LimitOffset, Cursor

class BookView(ViewSetMixin, CreateModelMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    pagination_class = Cursor

分页源码

def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)


def paginate_queryset(self, queryset):
   """
   Return a single page of results, or `None` if pagination is disabled.
   """
   if self.paginator is None:
       return None
   return self.paginator.paginate_queryset(queryset, self.request, view=self)


@property
def paginator(self):
    """
    The paginator instance associated with the view, or `None`.
    """
    if not hasattr(self, '_paginator'):
        if self.pagination_class is None:
            self._paginator = None
        else:
            self._paginator = self.pagination_class()
    return self._paginator

排序

查询所有才涉及到排序,其他的接口不需要排序

1, 视图类必须继承GenericAPIView需要用get方法

2, 在视图类中指定filter_backends属性,是一个列表里边放排序类

3, 在视图类中指定排序字段ordering_fields是一个列表可以房多个字段

from rest_framework.filters import OrderingFilter, SearchFilter

class BookView(ViewSetMixin, ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    filter_backends = [OrderingFilter]
    ordering_fields = ['price', id]

前端能支持的访问格式

http://127.0.0.1:8000/books/?ordering=price,id

先按照价格的升序排, 如果价格一样,按照id的升序排,-id是按照降序排

分页和排序可以一起使用, 先进行排序再分页

过滤

查询所有才会涉及到过滤

1, 视图类必须要继承GenericAPIView,要有get方法

2, 在视图类中指定filter_backends属性, 是一个列表里边放过滤器

3, 在视图类中指定排序字段search_fields,是哟个列表,可以放多个字段

from rest_framework.filters import OrderingFilter, SearchFilter

class BookView(ViewSetMixin, ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    filter_backends = [SearchFilter]
    search_fields = ['name']

前端支持的访问格式

http://127.0.0.1:8000/books/?search=xxx

内置的过滤如果配置多了个字段,过滤的条件是或的关系, 只要符合一个条件就可以

过滤类的其他使用

在内置的过滤类中,只能通过search=搜索条件,这种方式进行搜索,条件一=xxx&条件二=xxx
 
第三方过滤模块:django-filter
1.下载模块pip3 install django-filter,一定要注意,在下载第三方模块时,与其有关联的模块可能会跟着一起更新,所以需要检查一下,是否被自动更新至最新版本
 
2.导入该模块
from django_filters.rest_framework import DjangoFilterBackend
 
3.配置在试图类中
filter_backends = [DjangoFilterBackend,]
 
4.配置搜索的字段
filterset_fields = ['搜索条件一', '搜索条件二']
 
5.支持的搜索方式
http://127.0.0.1:8000/访问视图/?搜索条件一=xxx&搜索条件二=xxx
 
6.搜索模式
精准搜索,条件是并且的关系,两个搜索必须精确多一少一都不可,两个只要有一个错误那么就搜索不到
 
手搓,自定义过滤类
1.自己写一个类方法,但是必须要继承BaseFilterBackend
 
2.重写某个方法
from rest_framework.filters import BaseFilterBackend
 
class AsBaseFilterBackend(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        name = request.query_params.get('name',None)
        publish = request.query_params.get('publish', None)
        queryset = queryset.filter(name__icontains=name,publish__icontains=publish)
        return queryset
 
3.配置在视图类上
filter_backends = [AsBaseFilterBackend,]
多个过滤类和排序类可以共用,filter_backends=[],可以配置多个,执行顺序是从做往右,所以,放在最左侧的尽量先过滤掉大部分数据

过滤排序源码分析

首先回想我们的排序与过滤 想使用他们两个必须继承GenericAPIView + ListModelMixin及其子类 排序类OrderingFiler
只要在视图类中配置filter_backends、filterset_fields&search_fields就可以实现过滤和排序了 内置的过滤类SearchFilter
Django-filter 自定义写一个类 继承BaseFilterBackend 重写filter_queryset 返回的queryset对象 就是过滤或排序后的数据了
再次我们使用排序与过滤的时候只会在获取信息的时候才会用到 所以我们进到源码中查找list方法就好了

def list(self, request, *args, **kwargs):
        # self.get_queryset()所有数据,经过了self.filter_queryset返回了qs
        # self.filter_queryset完成的过滤
        queryset = self.filter_queryset(self.get_queryset())
        # 如果有分页,走的分页----》视图类中配置了分页类
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
	   # 如果没有分页,走正常的序列化,返回
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
    
    
    -self.filter_queryset完成了过滤,当前在视图类中,self是视图类的对象,去视图类中找没找到,去父类---》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
        
 '''
	-写的过滤类要重写filter_queryset,返回qs(过滤或排序后)对象
    -后期如果不写过滤类,只要在视图类中重写filter_queryset,在里面实现过滤也可以
 '''

异常处理

drf中无论是三大认证还是视图类的方法中执行,只要报错(主动抛异常),都能执行一个函数(异常处理的函数)
只要除了异常在APIView的dispatch中捕获了执行了配置文件中的配置REST_FRAMEWORK = {
'EXCEPTION_HANDLER':'app01.exctptions.common_exception_handler'
}
 

from rest_framework.views import exception_handler
from rest_framework.filters import SearchFilter
from rest_framework.response import Response
import time
import datetime
def common_exception_handler(exc,context):
    # 我们之后如果写代码的话,需要记录日志,只要进入了这里那么就一定是出现了异常,只要出现异常我们就可以到日志中寻找是哪里出现异常,也方便了纠错
    print('裂开来')
    print(exc)  # 查看exc是什么错误
    print(context)  # 查看内容
    request = context.get('request')  # 获取当前请求的request
    # 因为我们只能捕获外面的异常如果这里面有异常是捕获不到的,所以我们应该在内部也有异常捕获
    try:
        # 获取username
        username = request.user.username
    except:
        username = '没有登录'
    ctime = time.time()
    path = request.path
    method_type = request.method
    # 我们提示错误信息
    print(f'{username}用户,在{ctime}时间,访问{path}接口,通过{method_type}请求访问,除了错误:{str(exc)}')
    # 内置的这个异常处理函数只能处理drf的异常(继承了APIException的异常)
    response = exception_handler(exc, context)
    # 如果response有值,说明错误被处理了(Http404,PermissionDenied,APIException)
    if response:
        return Response({'code':701, 'msg':'drf错误,错误原因:%s'%response.data.get('detail','未知错误')})
    else:  # 如果没有值,这个错误就没有被处理,说明不是drf的错误,而是django的错误
        return Response({'code': 601, 'msg': '系统错误,错误原因:%s'% str(exc)})

异常捕获源码分析

# 我们在读APIView源码的时候 我们知道了APIView的dispatch中写了全局异常捕获只要是三大认证或则是视图类代码出错 都会被捕获到

# 然后我们可以看一下它是怎么捕获的
def dispatch(self, request, *args, **kwargs):
    try:
        self.initial(request, *args, **kwargs)
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(),
                              self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed

        response = handler(request, *args, **kwargs)
    except Exception as exc:
        response = self.handle_exception(exc)  # 上面只要出错都会返回错误 然后我们可以看一下handle_exception写了啥
        # 然后通过查找顺序 最后还是在APIView中找到了该方法
        

def handle_exception(self, exc):
    exception_handler = self.get_exception_handler()
    response = exception_handler(exc, context)  
    return response  
    # 我们可以知道该方法返回了response 然后我们就可以看到上面的最后的是get_exception_handler赋值给了response
    # 然后根据名称的查找顺序最后又是在APIVIew中找到了该方法 所以我们看一下get_exception_handler该方法写了啥

def get_exception_handler(self):
    return self.settings.EXCEPTION_HANDLER  
    # 该方法返回了配置文件中的EXCEPTION_HANDLER 
    # 然后根据查找顺序我们要去rest_framework中的settings中查找EXCEPTION_HANDLER是什么
    # 最后是这个 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' 可以知道是这个
    # 那么我们可以看一下exception_handler该方法写了啥
    from rest_framework.views import exception_handler
    
def exception_handler(exc, context):
    if isinstance(exc, Http404):  # 这个处理404异常
        exc = exceptions.NotFound()
    elif isinstance(exc, PermissionDenied):  # 这个是权限异常
        exc = exceptions.PermissionDenied()
    if isinstance(exc, exceptions.APIException):  
    # 这个是我们在写认证类抛异常的类AuthenticationFailed继承的类  可以理解为认证异常
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}
        set_rollback()
        return Response(data, status=exc.status_code, headers=headers)
        
    return None  # 这个None 就是除了drf的错误 其他错误没有处理
# 该方法就是全局异常最后调用的方法  

# 总结
    -只要出了异常在APIView的dispatch中捕获了,执行了配置文件中配置的:'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值