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全局配置(所有drf的异常,都会走这里)
'EXCEPTION_HANDLER': 'app01.app01_auth.my_exception_handler',
drf(过滤、排序、异常)的更多相关文章
- DRF 过滤排序分页异常处理
DRF 中如何使用过滤,排序,分页,以及报错了如何处理?10分钟get了~
- day74:drf:drf其他功能:认证/权限/限流/过滤/排序/分页/异常处理&自动生成接口文档
目录 1.django-admin 2.认证:Authentication 3.权限:Permissions 4.限流:Throttling 5.过滤:Filtering 6.排序:OrderingF ...
- drf-day7——认证组件、权限组件、频率组件、过滤排序、分页
目录 一.认证组件 1.1 登录接口 1.2 认证组件使用步骤 1.3 整体代码 1.4认证时cookie的获取方式 二.权限组件 2.1需求分析: 2.2 权限的使用 2.3代码 三.频率组件 3. ...
- ch7-列表渲染(v-for key 数组更新检测 显示过滤/排序结果)
1 说明 我们用 v-for 指令根据一组数组的选项列表进行渲染. v-for 指令需要以 item in items 形式的特殊语法, items 是源数据数组并且 item 是数组元素迭代的别名. ...
- CopyOnWriteArrayList集合排序异常问题
1.集合自定义排序实现 对List集合的自定义排序想必大家都知道要使用如下的方式,通过实现Comparator接口并实现compare方法来实现. /** * * @方法名 changeChain * ...
- Vue数组更新及过滤排序
前面的话 Vue为了增加列表渲染的功能,增加了一组观察数组的方法,而且可以显示一个数组的过滤或排序的副本.本文将详细介绍Vue数组更新及过滤排序 变异方法 Vue 包含一组观察数组的变异方法,它们将会 ...
- AngularJS过滤排序思路
本篇主要整理使用AngularJS进行过滤排序的思路. 在controller中,$scope的persons字段存储数组. $scope.persons = [ { "name" ...
- 三 drf 认证,权限,限流,过滤,排序,分页,异常处理,接口文档,集xadmin的使用
因为接下来的功能中需要使用到登陆功能,所以我们使用django内置admin站点并创建一个管理员. python manage.py createsuperuser 创建管理员以后,访问admin站点 ...
- DRF之过滤排序分页异常处理
一.过滤 对于列表数据要通过字段来进行过滤,就需要添加 django-filter 模块 使用方法: # 1.注册,在app中注册 settings.py INSTALLED_APPS = [ 'dj ...
- drf过滤和排序及异常处理的包装
过滤和排序(4星) 查询所有才需要过滤(根据过滤条件),排序(按某个规律排序) 使用前提: 必须继承的顶层类是GenericAPIView 内置过滤类 内置过滤类使用,在视图类中配置,是模糊查询 使用 ...
随机推荐
- Spring Boot 1.5.x 结合 JUnit5 进行接口测试
在Spring Boot 1.5.x中,默认使用Junit4进行测试.而在对Controller进行接口测试的时候,使用 @AutoConfigureMockMvc 注解是不能注入 MockMvc 对 ...
- 鹏程杯子2023 pwn
主要就是修改stdin的最后几位,使他变为write,然后泄露libc,为所欲为即可. 本人是卡在不知道stdin那里可以修改. 然后使用一下jmp qword rbp这个gadget 0x400a9 ...
- ES6学习 第一章 let 和 const 命令
前言: 最近开始看阮一峰老师的<ECMAScript 6 入门>(以下简称原文)学习ECMAScript 6(下文简称ES6)的知识,整理出一些知识点加上我的理解来做成文章笔记.按照章节为 ...
- NC50493 石子合并
题目链接 题目 题目描述 将n堆石子绕圆形操场排放,现要将石子有序地合并成一堆.规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分. 请编写一个程序,读入堆数n及每堆的石子 ...
- NC16696 [NOIP2001]统计单词个数
题目链接 题目 题目描述 给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个).要求将此字母串分成k份( 1 < k ≤ 40 ...
- idea 灵异事件之maven 缓存
方法一 mvn clean install 方法二 强制刷新maven 1 idea 右侧的maven 窗口: 方法三 强制刷新maven 2 右键项目: 上面虽然是重新导入Maven依赖,按理说,I ...
- Swift —— 一、架构解析
一.简介 OpenStack 对象存储 (swift) 用于冗余.可扩展的数据 使用标准化服务器集群存储PB的存储 可访问的数据.它是一种长期存储系统,可存储大量 可以检索和更新的静态数据.对象存储使 ...
- Spring和Spring Boot的区别
spring Spring 是一个开源轻量级框架,它允许 Java EE 7 开发人员构建简单.可靠和可扩展的企业应用程序.该框架主要侧重于提供各种方法来帮助您管理业务对象.与 Java 数据库连接 ...
- 我的小程序之旅七:微信公众号设置IP白名单
一.为什么要配置IP白名单 此处IP为服务器对公网IP: 在IP白名单内的IP地址作为来源,获取access_token接口才可调用成功. 而想要调用公众号相关API,就必须获取access_toke ...
- win32 - wsprintf和wvsprintf
前者很常用, 经常被用来转换为字符串或者拼接字符串. 例子: #include <Windows.h> #include <stdio.h> int main() { int ...