重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
过滤是涉及到查询数据的接口才需要过滤功能
公司主营业务:网站制作、网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联推出正定免费做网站回馈大家。
DRF 中使用的过滤方式:
使用模块: from rest_framework.filters import SearchFilter
在视图层中使用内置过滤类
前提:需要使用 GenericAPIView
类中的filter_backends
属性,所以视图类得继承 GenericAPIView
class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
lookup_field = 'pk'
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
步骤:
from rest_framework.filters import SearchFilter
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
# throttle_classes = [IPThrottling, ]
filter_backends = [SearchFilter]
search_fields = ['name', 'price', ]
使用双下滑的正向查询方式
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
# throttle_classes = [IPThrottling, ]
filter_backends = [SearchFilter, ]
search_fields = ['publish__name', ]
总结:
search
1、安装: pip install django-filter
2、使用模块: from django_filters.rest_framework import DjangoFilterBackend
3、在项目配置文件 settings.py 中注册下载的 app
4、第三方过滤类在filter_backends
字段中写,filter_fields
字段指定过滤的字段
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用,
]
4、视图层中使用
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
filter_backends = [DjangoFilterBackend, ]
filter_fields = ['name', 'price']
总结:
filter_backends
字段中写,filter_fields
字段指定过滤的字段GenericAPIView
才能使用&
来表示和的关系外键字段怎么查?
1、新建一个过滤文件,写一个类继承BaseFilterBackend
,重写filter_queryset(self, request, queryset, view)
方法,返回queryset对象,qs对象是过滤后的
2、视图层使用,只需要指定filter_backend
属性为自定义类列表
3、查询过滤,支持模糊查询(自己定制过滤方式),在filter_queryset
方法自定义过滤规则
自定义过滤类的书写:
from rest_framework.filters import BaseFilterBackend
from django.db.models import Q
class Myfilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 获取过滤参数
qs_name = request.query_params.get('name')
qs_price = request.query_params.get('price')
# title__contains:精确大小写查询,SQL中-->like BINARY
# 利用Q查询构造或关系
if qs_name:
queryset = queryset.filter(name__contains=qs_name)
elif qs_price:
queryset = queryset.filter(price__contains=qs_price)
elif qs_name or qs_price:
queryset = queryset.filter(Q(name__contains=qs_name) | Q(price__contains=qs_price))
return queryset
视图类:
from app01.filter import Myfilter
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
filter_backends = [Myfilter, ]
我们知道过滤的前提条件是视图继承了GenericAPIView才能使用,那么在GenericAPIView中的执行流程是什么?
1、调用了GenericAPIView中的filter_queryset方法
2、filter_queryset方法源码:
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
'''
1.backend是通过遍历该类的filter_backends列表的得到的,也就是我们指定的过滤类列表,那么backend就是我们的过滤类
2.通过实例化得到对象来调用了类内的filter_queryset返回了过滤后的对象
'''
使用模块:from rest_framework.filters import OrderingFilter
步骤:
filter_backends
属性的列表中ordering_fields
指定要排序的字段-
号代表倒序,且必须使用ordering
指定排序字段视图类书写:
from app01.filter import Myfilter
from rest_framework.filters import OrderingFilter
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
# throttle_classes = [IPThrottling, ]
filter_backends = [Myfilter, OrderingFilter, ] # 先过滤后排序
ordering_fields = ['id', 'price']
注意:过滤可以和排序同时使用,但是先执行过滤再执行排序,提升了代码的效率(先过滤后排序),因为如果先排序,那么数据库的数量庞大的话,直接操作了整个数据库,消耗资源,过滤完成后排序只是针对一小部分数据
分页只在查询所有接口中使用
导入分页类: from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
DRF 中分页的三种方式:
步骤:
自定义类,继承PageNumberPagination
,重写四个类属性
注意: 配置在视图类中,通过pagination_class
指定,必须继承GenericAPIView才有
分页类书写:
from rest_framework.pagination import PageNumberPagination
class BookPagination(PageNumberPagination):
page_size = 3 # 默认每页显示2条
page_query_param = 'page' # 查询条件,eg:page=3
page_size_query_param = 'size' # 查询条件参数size=5显示五条
max_page_size = 10 # 每页最大显示条数
视图层类:
给 pagination_class
属性赋值分页类
from app01.page import BookPagination
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
authentication_classes = []
filter_backends = [Myfilter, OrderingFilter, ] # 先过滤后排序
ordering_fields = ['id', 'price']
pagination_class = BookPagination
步骤:
分页类书写:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
class MyLimitOffset(LimitOffsetPagination):
default_limit = 2 # 默认每页显示2条
limit_query_param = 'limit' # ?limit=3,查询出3条
offset_query_param = 'offset' # 偏移量,?offset=2,从第2条后开始
max_limit = 5 # 最大显示5条
视图层类:
给 pagination_class
属性赋值分页类
from app01.page import BookPagination, MyLimitOffset
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
authentication_classes = []
filter_backends = [Myfilter, OrderingFilter, ] # 先过滤后排序
ordering_fields = ['id', 'price']
pagination_class = MyLimitOffset
步骤:
分页类书写:
from rest_framework.pagination import CursorPagination
class MyCursor(CursorPagination):
page_size = 3
cursor_query_param = 'cursor'
ordering = 'id'
视图层类:
给 pagination_class
属性赋值分页类
from app01.page import BookPagination, MyLimitOffset, MyCursor
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
authentication_classes = []
filter_backends = [Myfilter, ] # 使用了 cursor 游标分页,不要指定排序规则,会报错
ordering_fields = ['id', 'price']
pagination_class = MyCursor
查询方式
http://127.0.0.1:8000/books/?cursor=cD02
注意:分页类内指定了排序,视图内不要写排序规则,不然报错
之前读APIView源码的时候,捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获
以下是APIView
捕获异常 的流程
1、 APIView源码
# dispatch方法源码
except Exception as exc:
response = self.handle_exception(exc)
# handle_exception方法源码
exception_handler = self.get_exception_handler()
response = exception_handler(exc, context)
2、 默认配置文件
get_exception_handler() 调用的是 views 中的 exception_handler
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
3、views种的exception_handler方法
def exception_handler(exc, context):
if isinstance(exc, Http404):
exc = exceptions.NotFound()
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
else:
data = {'detail': exc.detail}
return Response(data, status=exc.status_code, headers=headers)
return None
由上源码可知,exception_handler(exc, context) 方法,如果报的是已知的错会返回 Response 对象,未知错误返回 None
可以自定义出现异常之后的处理方法和返回数据的格式
重写异常处理方法:
from rest_framework.views import exception_handler
from rest_framework.response import Response
def myexception_handler(exc, context):
# 先执行原来的exception_handler帮助我们处理
res = exception_handler(exc, context)
if res:
# res有值代表处理过了APIException对象的异常了,返回的数据再定制
res = Response(data={'code': 998, 'msg': res.data.get('detail', '服务器异常,请联系系统管理员')})
# res = Response(data={'code': 998, 'msg': '服务器异常,请联系系统管理员'})
# res.data.get从响应中获取原来的处理详细信息
else:
res = Response(data={'code': 999, 'msg': str(exc)})
print(exc) # list index out of range
'''模拟日志处理'''
request = context.get('request') # 当次请求的request对象
view = context.get('view') # 当次执行的视图类对象
print('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method))
'''结果:
错误原因:list index out of range,错误视图类:,请求地址:/test/,请求方式:GET
'''
return res
修改异常的配置路径:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'app01.myexception.exception_handler' # 再出异常,会执行自己定义的函数
}
视图类中报错就会自动触发异常处理:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
# 测试异常视图
class Test(APIView):
def get(self,request):
# 1、 其他报错
# l = [1,2,3]
# print(l[100])
# 2、APIException异常
# raise APIException('APIException errors!')
return Response('success!')