首页 > 其他分享 >drf(过滤、排序、异常)

drf(过滤、排序、异常)

时间:2023-09-26 20:34:56浏览次数:26  
标签:exception exc handler self django filter 过滤 排序 drf

一. 过滤组件

1 内置过滤组件SearchFilter

# 缺点: 外键字段的搜索操作将会抛出异常: Related Field got invalid lookup: icontains

# 1)在视图文件views.py中导入drf的搜索组件
from rest_framework.filters import SearchFilter

# 2)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [SearchFilter]

# 3)配置视图类关联的Model表参与搜索的字段
search_fields = ['name', 'id']

# 4)前台访问该群查接口,采用拼接参数方式用search关键字将搜索目标提供给后台
http://127.0.0.1:8000/course/free/?search=2  # id或name中包含2的所有结果

 2 第三方过滤组件django-filter

使用

1. 安装:pip3 install django-filter
2. 注册: settings.py中注册
    INSTALLED_APPS = [
        ...
        'django_filters',  # 需要注册应用,
    ]
    
3. 全局配置 或者 局部配置
    全局配置: 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
    局部配置:
        指定所有字段: filterset_fields = '__all__'
        指定固定字段: filterset_fields = ['name', ...]   # 提示: 可以元组, 也可以是列表
# 介绍: 争对django内置搜索组件的拓展, 在django内置的基础之上还拓展了外键字段的过滤功能.
# 前提:安装django-filter插件
pip install django-filter  (注意: 不要安装成了django-filters)

"""方式一"""
# 1)在视图文件views.py中导入django-filter的功能组件
from django_filters.rest_framework import DjangoFilterBackend

# 2)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]

# 3)配置视图类关联的Model表可以分类的字段(通常是可以分组的字段)
filterset_fields = ['course_category']

# 4)前台访问该群查接口,采用拼接参数方式用分类course_category字段将分类条件提供给后台
http://127.0.0.1:8000/course/free/?course_category=1  # 拿课程分类1下的所有课程

'''方式二'''
# 1)自定义过滤类继承django-filter插件的FilterSet类,绑定Model表,并设置分类字段
from django_filters.filterset import FilterSet
from . import models
class CourseFilterSet(FilterSet):
    class Meta:
        model = models.Course
        fields = ['course_category']

# 2)在视图文件views.py中导入django-filter的功能组件及自定义的过滤类
from django_filters.rest_framework import DjangoFilterBackend
from .filters import CourseFilterSet
        
# 3)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]

# 4)配置视图类关联的自定义过滤类
filter_class = CourseFilterSet

# 5)前台访问该群查接口,采用拼接参数方式用分类course_category字段将分类条件提供给后台
http://127.0.0.1:8000/course/free/?course_category=1  # 拿课程分类1下的所有课程

3. django-filter实现区间过滤

# 1)自定义过滤类继承django-filter插件的FilterSet类,绑定Model表,并设置自定义区间规则字段
from django_filters.filterset import FilterSet
from . import models
class CourseFilterSet(FilterSet):
    # 区间过滤: students学生中总人数要大于等于min_students, 要小于等于max_students. [min_students, max_students]
    max_students = filters.NumberFilter(field_name='students', lookup_expr='lte')
    min_students = filters.NumberFilter(field_name='students', lookup_expr='gte')

    class Meta:
        model = Course
        fields = ['course_category', 'students', 'min_students', 'max_students']

# 2)在视图文件views.py中导入django-filter的功能组件及自定义的过滤类
from django_filters.rest_framework import DjangoFilterBackend
from .filters import CourseFilterSet
        
# 3)将搜索组件配置给群查接口视图类的filter_backends
filter_backends = [DjangoFilterBackend]

# 4)配置视图类关联的自定义过滤类
filter_class = CourseFilterSet

# 5)前台访问该群查接口,采用拼接参数方式用自定义区间规则字段将区间条件提供给后台
http://127.0.0.1:8000/course/free/?min_students=230&max_students=250  # 获取学生总人数230~250之间的数据        

4. 自定义过滤

# filters.py
from rest_framework.filters import BaseFilterBackend


class CustomFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 老师的模糊匹配
        name = request.GET.get('teacher')
        if not name:
            return queryset
        teacher_queryset = queryset.filter(teacher__name__contains=name)
        return teacher_queryset
    
# views.py
# 自定义过滤: 通过老师名进行模糊匹配
filter_backends = [CustomFilter]

5. 注意

django-filter的安装可能会出现django版本最低要求问题,  如果下载最新版本的django-filter
如果使用的是django 1.11版本会自动升级到3.x,并使用filterset_fileds

二. 排序组件

1. 全局配置 局部配置

# 全局配置
    # 排序
    REST_FRAMEWORK = {
            'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.OrderingFilter')
        }

    # 过滤 和 排序
        REST_FRAMEWORK = {
            'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter')
        }


# 局部配置
    # 排序
        from rest_framework.filters import OrderingFilter
        filter_backends = [OrderingFilter]  # 注意: 如果这样就会覆盖全局配置配置的过滤

    # 过滤 和 排序
        from rest_framework.filters import OrderingFilter
        from django_filters.rest_framework import DjangoFilterBackend
        filter_backends = [OrderingFilter, DjangoFilterBackend]
        filterset_fields = '__all__'

2. 代码实例

class TextView7(ListAPIView):
    # 局部将全局可能配置的认证+权限+频率禁用
    authentication_classes = []
    permission_classes = []
    throttle_classes = []

    # 局部配置排序组件.
    # 注意: 如果要过滤和排序, 需要注意的是如果全局配置了过滤, 需要在声明排序的基础之上再什么过滤. 因为filter_backends的局部指定会覆盖过滤的配置.
    # filter_backends = [OrderingFilter, DjangoFilterBackend, ]  # 提示: 2者之间没有顺序
    filter_backends = [DjangoFilterBackend, OrderingFilter]

    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    filterset_fields = ['name', 'price']  # 可以用列表, 也可以用元组
from rest_framework.generics import ListCreateAPIView
from app01 import models
from app01.ser import BookSerializer

from django_filters.rest_framework import DjangoFilterBackend
from  rest_framework.filters import OrderingFilter

# 排序
class BookView1(ListCreateAPIView):
    # authentication_classes = []

    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter, DjangoFilterBackend]
    filterset_fields = ['title', ]
    ordering_fields = ('id', 'price')
   
# # 127.0.0.1:8000/books1/?ordering=-id
# -id 表示针对id字段进行倒序排序
# id  表示针对id字段进行升序排序

# 可以结合过滤一起使用

3. 总结

# 过滤导入
from django_filters.rest_framework import DjangoFilterBackend
# 排序导入
from rest_framework.filters import OrderingFilter
# 注意问题
它们2个全局配置都是共用一个配置路径, 如果局部指定了就会将全局配置的对应项所有的覆盖

三. 异常处理

1. 从源码分析到如何实现自定义异常处理

# 思路: 发现有些错误被drf捕获了, 而有些错误会交给django自己处理, 这是为什么呢? 源码分析一波

# 查找路径: APIView -> dispatch -> try..except -> handle_exception

# 源码分析:
    try:
        self.initial(request, *args, **kwargs)

        # Get the appropriate handler method
        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)   # 这里

    1. 异常的捕获范围: 注意, 并不是所有位置的异常出可以捕获. 例如: 自定义视图中的类中抛出的异常就不行
        self.initial(request, *args, **kwargs)
            认证: self.perform_authentication(request)
                提示: 不会捕获自定义的认证类. 因此perform_authentication做的事情就是request.user赋值
            权限: self.check_permissions(request)
            频率: self.check_throttles(request)
            自定义视图类中的方法:
                response = handler(request, *args, **kwargs)
    2. 关键实现 handle_exception方法
        def handle_exception(self, exc):
            # 1) 这里的在认证失败的时候会走
            if isinstance(exc, (exceptions.NotAuthenticated,
                                exceptions.AuthenticationFailed)):
                # WWW-Authenticate header for 401 responses, else coerce to 403
                auth_header = self.get_authenticate_header(self.request)

                if auth_header:
                    exc.auth_header = auth_header
                else:
                    exc.status_code = status.HTTP_403_FORBIDDEN

            # 2) 这里就是通过配置文件配置的路径, 拿到处理异常的函数, 内部就一句代码: self.settings.EXCEPTION_HANDLER
            '''
            def get_exception_handler(self):
                """
                配置文件导入的内容:
                'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
                """
                return self.settings.EXCEPTION_HANDLER
            '''
            exception_handler = self.get_exception_handler()

            # 3) 获取异常的处理的上下文内容, 本质里面就是获取操作的视图对象的结果
            '''
            def get_exception_handler_context(self):
                return {
                    'view': self,
                    'args': getattr(self, 'args', ()),
                    'kwargs': getattr(self, 'kwargs', {}),
                    'request': getattr(self, 'request', None)
                }
            '''
            context = self.get_exception_handler_context()

            # 4) 将刚刚从配置文件中导入的视图函数传参调用
            '''
            exc: 这里的exc是APIView中定义的dispatch中传过来的异常对象
            context: 这里的context是对出现异常对象的上下文捕获
            '''
            response = exception_handler(exc, context)

            # 5) 关键转折:
            '''
            这里就是通过在drf提供的exception_handler函数处理的返回值结果来判断时候交给django自己处理.
            如果response的返回值是None就会交给django处理了, 现在我们要的就是在exception_handler函数执行完毕以后将返回值进行判断,
            并且返回的结果不再是None, 而应该是response对象
            '''
            if response is None:
                self.raise_uncaught_exception(exc)

            response.exception = True
            return response

# 步骤:
    1. 先新建一个.py文件存放自定义的异常处理函数
    2. 在drf提供的默认配置文件中导入exception_handler函数的
    3. 在自定义异常处理函数中先将exception_handler传入让drf先处理一番, 根据返回的结果为None是来执行自己的判断.
        如果返回不为None也不应该直接将原本的response对象直接返回, 可以自己封装一个符合restful规范的类用来继承Response类
        将原本的response对象中的返回结果通过 response.data.get('detail') 方法获取
    4. settings.py文件中配置自定义的exception_handler函数的路径
        'EXCEPTION_HANDLER': 'app01.app_auth.custom_exception_handler',

2. 代码实例

自定义utils.py

from rest_framework.views import exception_handler
from rest_framework import status

from rest_framework.response import Response


class APIResponse(Response):
    def __init__(self, code=1000, messages='成功', results=None, error=None,
                 status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None, **kwargs):
        data = {
            'code:': code,
            'messages:': messages,
        }
        print('error:', error)
        print('results:', results)
        if results:
            data['results'] = results
        if error:
            data['error'] = error
        data.update(kwargs)

        super().__init__(data=data, status=status,
                         template_name=template_name, headers=headers,
                         exception=exception, content_type=content_type)


def custom_exception_handler(exc, context):
    """
    :param exc: 这里的exc是APIView中定义的dispatch中传过来的异常对象
        try:
            ...
        except Exception as exc:
            response = self.handle_exception(exc)
    :param context: 这里的context是对出现异常对象的上下文捕获
        查找: handle_exception -> get_exception_handler_context
            def get_exception_handler_context(self):
                return {
                    'view': self,
                    'args': getattr(self, 'args', ()),
                    'kwargs': getattr(self, 'kwargs', {}),
                    'request': getattr(self, 'request', None)
                }
    :return: 这里返回Response对象, 本来drf没有处理的的异常会交给django处理, 但是我们捕获这种异常, 规定成统一的处理. 让drf处理.

    注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    这里可以捕获的异常范围由以下源码得知:
        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            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)

    捕获范围:
        self.initial(request, *args, **kwargs)
            认证: self.perform_authentication(request)
                提示: 不会捕获自定义的认证类. 因此perform_authentication做的事情就是request.user赋值
            权限: self.check_permissions(request)
            频率: self.check_throttles(request)
            自定义视图类中的方法:
                response = handler(request, *args, **kwargs)
    """
    obj = None
    response = exception_handler(exc, context)
    # 注意: exc, context都不是可json序列化的格式, 需要转换成字符串类型.
    if not response:
        # 自己的处理
        if isinstance(exc, AttributeError):
            obj = APIResponse(2000, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
        elif isinstance(exc, ImportError):
            obj = APIResponse(2002, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
        elif isinstance(exc, TypeError):
            obj = APIResponse(2003, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
        elif isinstance(exc, Exception):
            obj = APIResponse(2004, '失败', error=str(exc), results=str(context), status=status.HTTP_403_FORBIDDEN)
    else:
        # 在drf处理的基础之上再次处理
        obj = APIResponse(2005, '失败', error=response.data.get('detail'), results=str(context),
                          status=status.HTTP_403_FORBIDDEN)
    return obj

3. 总结

1. 导入需要在drf提供的默认函数的基础之上的函数
    from rest_framework.views import exception_handler
2. 自定义异常处理函数2个参数exc, context
3. 先让drf处理一波, 处理它处理不完的, 或者 在他处理完的基础之上拓展, 通过response返回结果来进行区分
    提示: 可以通过 response.data.get('detail') 获取drf处理完的对象中返回的响应信息

4. 注意

配置文件中配置自定义的异常处理函数时, drf提供的exception_handler的导入会与在同一个文件中自定义的认证类 或者 自定义的权限类的导入起冲突.
自定义的异常的处理代码逻辑最好新建一个纯净的.py文件存放

5. 快速使用

from rest_framework.views import exception_handler
from rest_framework.response import Response


class CommonResponse(Response):
    def __init__(self, code=1000, messages='ok', results=None,
                 status=None, template_name=None, headers=None,
                 exception=False, content_type=None,
                 **kwargs):
        data = {
            'code': code,
            'messages': messages,
        }
        data.update(kwargs)
        if results:
            data['results'] = results
        super().__init__(data=data, status=status,
                         template_name=template_name, headers=headers,
                         exception=exception, content_type=content_type)



def common_exception_handler(exc, context):
    response = exception_handler(exc, context)
    if not response:
        obj = CommonResponse(code=2000, messages='error', results=str(exc))
    else:
        obj = CommonResponse(code=2000, messages='error', results=response.data)
    return obj

settings.py全局配置

'EXCEPTION_HANDLER': 'app01.app01_auth.my_exception_handler',

 

标签:exception,exc,handler,self,django,filter,过滤,排序,drf
From: https://www.cnblogs.com/coderxueshan/p/17731071.html

相关文章

  • drf 测试(车型、车场、经销商)
    一、实现要求1有车型(CarModel),车厂(CarFactory),经销商(Distributor)三个表,一个车厂可以生产多种车型,一个经销商可以出售多种车型,一个车型可以有多个经销商出售车型:车型名,车型出厂价,车厂id车厂:车厂名,车厂地址,联系电话经销商:经销商名,地址,联系电话2有用户表,基于django内......
  • drf(认证、权限、频率)
    一.认证组件1.流程1.写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面.认证通过,返回两个值,一个值最终给了包装以后的request对象,视图中就可以通过request.user获取,认证失败,抛异常:APIException或者AuthenticationFailed注意:本质......
  • 数据库中order by 依照指定顺序排序如何操作
    SQL学习之使用orderby依照指定顺序排序或自己定义顺序排序 我们通常须要依据客户需求对于查询出来的结果给客户提供自己定义的排序方式,那么我们通常sql须要实现方式都有哪些,參考很多其它资料总结例如以下:一、假设我们仅仅是对于在某个程序中的应用是须要依照例如以下的方......
  • drf(路由)
    一自动生成路由基本使用#视图类,继承了ViewSetMixein,路由#path('books/',views.BookViewSet.as_view(actions={'get':'list','post':'create'})),#path('book/<int:pk>/',#views.BookViewSet.as......
  • drf(视图组件)
    一.前言DjangoRESTframwork提供的视图的主要作用1.控制序列化器的执行(检验、保存、转换数据)2.控制数据库查询的执行二.两个视图基类两个视图基类:APIView,GenericAPIView1.APIView继承django原生View,重写方法,去掉csrf验证,用自己的dispatchmodels.pyclassB......
  • 排序
    排序算法哪些是稳定的排序算法,哪些是不稳定的稳定的:直接插入排序:最坏情况是逆序,时间复杂度是O(N2),最好情况是插入的都是顺序,时间复杂度O(N),空间复杂度O(1)冒泡排序:时间复杂度O(N2),空间复杂度O(1)计数排序:时间复杂度O(N+Range),空间复杂度O(range)不稳定:希尔排序:时间复杂度O(N......
  • 如何实现一个数组按照另外一个数组的顺序进行排序?
    数组arr1按照arr2的顺序展示,如何实现:一、简单类型数组letarr1=[1,2,3,4,5] letarr2=[5,3,2,4,1]arr1.sort((prev,next)=>{ returnarr2.indexOf(prev)-arr2.indexOf(next)})console.log(arr1)//[5,3,2,4,1]二、复杂类型数组letarr1=[{......
  • lambda HashMap 排序
    目录TreeMaplambdacomparingByKey示例代码TreeMap按key排序生成map可以有TreeMap完成,TreeMap可以按key的自然顺序排序(Comparable实现)lambdacomparingByKey使用lambda也可以很方便的对map排序Map.Entry.comparingByKey()按key排序的ComparatorMap.Entry.comparingBy......
  • 18-过滤器
    过滤器(Vue3已经移除)定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理) 注册过滤器://全局过滤器Vue.filter(name,callback)//局部过滤器newVue{filters:{}} 使用过滤器:{{xxx|过滤器名}}或v-bind:属性="xxx|过滤器名" 备注:1)过滤器......
  • 快速排序/选择算法
    ......