django-rest-framework 基础四 过滤、排序、分页、异常处理
django-rest-framework 基础四 过滤、排序、分页、异常处理
1. 过滤
在之前所写的五个接口中,只有获取所有需要过滤,其他接口都不需要。如在访问的时候带参数过滤出自己想要的数据。
http://127.0.0.1:8080/?search=活着
1.1 内置过滤类
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from rest_framework.filters import SearchFilter
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter,]
# 过滤name
# search_fields = ['name']
# 过滤namt或author
search_fields = ['name','author']
路由urls.py
from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers
router = routers.SimpleRouter()
router.register('books', views.BookView,"books")
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls))
]
访问(模糊匹配):
http://127.0.0.1:8000/books/?search=西游记
http://127.0.0.1:8000/books/?search=华


1.2 第三方过滤类
使用第三方过滤类,
第一步先安装django-filter
pip install django-filter
第二步在配置里注册settings.py
INSTALLED_APPS = [
...
'rest_framework',
'django_filters',
]
第三步在视图类中使用views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [DjangoFilterBackend,]
filter_fields = ['name','author']
路由还是原来的配置。
这时访问是要过滤,关键字不能写search了,写search会把全部都打印出来,要写具体的字段名,而且后面要过滤的内容是精准匹配
http://127.0.0.1:8000/books/?name=西游记, 如果像之前直接写西则匹配不出来

http://127.0.0.1:8000/books/?name=活着&author=余华 # 这里面是and的关系,name是活着并且author是余华的

1.3 自定义过滤类
单独写一个类继承BaseFilterBackend 基类,重写filter_queryset方法,返回queryset对象
filter.py
from rest_framework.filters import BaseFilterBackend
class BookFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
query = request.query_params.get('name')
if query:
queryset = queryset.filter(name__contains=query)
return queryset
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from 应用名.filter import BookFilter
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
# filter_backends = [DjangoFilterBackend,]
filter_backends = [BookFilter,]
由于只写了name字段,所以只能匹配name
http://127.0.0.1:8000/books/?name=西 # 模糊匹配 ,自己定义的


2. 排序
可以使用DRF内置的OrderingFilter类进行排序。
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from rest_framework.filters import OrderingFilter
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter,]
ordering_fields=['price'] # 按价格排序
访问:
http://127.0.0.1:8000/books/?ordering=price # 正序

http://127.0.0.1:8000/books/?ordering=-price # 倒序, 使用减号(-)为倒序

可以把过滤和排序放在一块
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from rest_framework.filters import SearchFilter
from rest_framework.filters import OrderingFilter
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter,OrderingFilter,]
# 排序
ordering_fields=['price', 'id']
# 过滤namt或author
search_fields = ['name','author']


3. 分页
接口中也只有查询所有用到了分页。
默认的三种分页方法
3.1 方法一:基本分页PageNumberPagination
基本分页,按照页码数,每页显示多少条
单独创建一个文件专门用来分页:page.py
# 继承 PageNumberPagination,然后重写四个属性
from rest_framework.pagination import PageNumberPagination
class commPageNumberPagination(PageNumberPagination):
page_size= 3 # 默认每页显示的条数
page_query_param = 'page' # 查询条件为page, 如:?page=3
page_size_query_param ='size' # 每页显示的条数的查询条件 ?page=3&size=9 查询第三页,第三页显示9条
max_page_size = 5 # 每页最多显示几条, ?page=3&size=9,最终还是显示5条
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from 应用名.page import commPageNumberPagination
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = commPageNumberPagination
路由不变,默认访问:
http://127.0.0.1:8000/books/
可看到默认显示3条,

访问第一页,每页显示9条。
http://127.0.0.1:8000/books/?page=1&size=9
由于设置了最多显示5条,所以虽然设置了要显示9条,但最多也是显示5条

3.2 方法二:偏移分页 LimitOffsetPagination
page.py
from rest_framework.pagination import LimitOffsetPagination
class commLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 # 默认一页获取条数 3 条
limit_query_param = 'limit' # ?limit=3 获取三条,如果不传,就用上面的默认两条
offset_query_param = 'offset' # ?limit=3&offset=2 从第2条开始,获取3条 ?offset=3:从第三条开始,获取2条
max_limit = 4 # 最大显示条数 4 条
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from 应用名.page import commLimitOffsetPagination
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = commLimitOffsetPagination
访问:
http://127.0.0.1:8000/books/

从第二条开始,每页显示三条

3.3 方法三 游标分页 CursorPagination
page.py
from rest_framework.pagination import CursorPagination
class commCursorPagination(CursorPagination):
page_size = 3 # 每页显示2条
cursor_query_param = 'cursor' # 查询条件 ?cursor=sdafdase
ordering = 'id' # 排序规则,使用id排序
views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin
from 应用名.models import Book # 数据库
from 应用名.serializer import BookSerializer # 序列化器
from authenticated.page import commCursorPagination
class BookView(GenericViewSet, ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = commCursorPagination
访问:
http://127.0.0.1:8000/books/

3.4 三种分页总结
使用这三种分页视图类上,必须继承GenericAPIView
前面两种可以从中间位置获取某一页,但是游标分页方式只能上一页和下一页
前面两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据
游标分页方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页,在大数据量和app分页时,下拉加载下一页,不需要指定跳转到第几页
4. 异常处理
DRF中捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获。
如果自己要自己处理异常,则需要考虑:统一返回格式,无论是否异常,返回的格式统一 ,记录日志(好排查)
异常:
{code:999,msg:服务器异常,请联系系统管理员}
成功:
{code:100,msg:成功,data:[{},{}]}
4.1 自己处理异常
写一个视图函数
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def get(self,request):
# 第一:程序出错
l=[1,2,3]
print(l[99])
return Response('ok')
配置路由
urls.py
from django.contrib import admin
from django.urls import path,include
from authenticated import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.TestView.as_view()),
]
使用自带的异常处理时:
http://127.0.0.1:8000/test/

自己处理异常
第一步:创建一个专门处理的文件,里面写处理异常的代码。
excepotion.py
from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它
from rest_framework.response import Response
def common_exception_handler(exc, context):
# 第一步,先执行原来的exception_handler
# 第一种情况,返回Response对象,这表示已经处理了异常,它只处理APIExcepiton的异常,第二种情况,返回None,表示没有处理
res = exception_handler(exc, context)
if not res:
# 执行这里就说明res为None,它没有处理异常
res = Response(data={'code': 1001, 'msg': str(exc)})
return res
return res
# 注意:咱们在这里,可以记录日志---》只要走到这,说明程序报错了,记录日志,以后查日志---》尽量详细
# 出错时间,错误原因,哪个视图类出了错,什么请求地址,什么请求方式出了错
request = context.get('request') # 这个request是当次请求的request对象
view = context.get('view') # 这个viewt是当次执行的视图类对象
print('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method))
return res
### 以后再出异常,都会走这个函数,后期需要记录日志,统一了返回格式
第二步:把函数配置在配置文件中settings.py
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'authenticated.exceptions.common_exception_handler',# 再出异常,会执行这个函数
}
# authenticated 为应用名
第三步:测试
写视图类故意有程序错误:
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def get(self,request):
# 第一:程序出错
l=[1,2,3]
print(l[99])
return Response('ok')
访问:http://127.0.0.1:8000/test/

写视图类主动拋异常:
from rest_framework.views import APIView
from rest_framework.response import Response
class TestView(APIView):
def get(self,request):
raise Exception('程序异常,请联系管理员')
return Response('ok')

写视图类主动拋APIException异常
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
class TestView(APIView):
def get(self,request):
raise APIException('APIException异常')
return Response('ok')
访问发现这个里面就没有code,因为这是一个APIException异常,DRF会捕捉到。

如果APIException也想自己处理:
excepotion.py
from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它
from rest_framework.response import Response
def common_exception_handler(exc, context):
# 第一步,先执行原来的exception_handler
# 第一种情况,返回Response对象,这表示已经处理了异常,它只处理APIExcepiton的异常,第二种情况,返回None,表示没有处理
res = exception_handler(exc, context)
if not res:
# 执行这里就说明res为None,它没有处理异常
res = Response(data={'code': 1001, 'msg': str(exc)})
return res
# 如果执行到这说明res内容,返回的是Response对象, res.data.get('detail', '请联系管理员')表示如果detail里面有内容,则用途detail里面的,没有则使用'请联系管理员'
res = Response(data={'code': 1002, 'msg': res.data.get('detail', '请联系管理员')})
return res

没用设置则显示自己处理时写的:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
class TestView(APIView):
def get(self,request):
raise APIException()
return Response('ok')

4.2 出现异常记录日志
excepotion.py
from rest_framework.views import exception_handler # 默认没有配置,出了异常会走它
from rest_framework.response import Response
def common_exception_handler(exc, context):
# 第一步,先执行原来的exception_handler
# 第一种情况,返回Response对象,这表示已经处理了异常,它只处理APIExcepiton的异常,第二种情况,返回None,表示没有处理
res = exception_handler(exc, context)
if not res:
# 执行这里就说明res为None,它没有处理异常
res = Response(data={'code': 1001, 'msg': str(exc)})
return res
# 如果执行到这说明res内容,返回的是Response对象, res.data.get('detail', '请联系管理员')表示如果detail里面有内容,则用途detail里面的,没有则使用'请联系管理员'
res = Response(data={'code': 1002, 'msg': res.data.get('detail', '请联系管理员')})
# 记录日志
request = context.get('request') # 这个request是当次请求的request对象
view = context.get('view') # 这个viewt是当次执行的视图类对象
print('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method))
return res
访问时后台会把错误信息打印出来:
错误原因:服务器出现了错误。,错误视图类:<authenticated.views.TestView object at 0x000002A296C92560>,请求地址:/test/,请求方式:GET
这里是吧错误信息打印出来了,正确做法是把它记录到一个日志文件里面,具体的可以使用python的日志模块logging
django-rest-framework 基础四 过滤、排序、分页、异常处理的更多相关文章
- DRF 过滤排序分页异常处理
DRF 中如何使用过滤,排序,分页,以及报错了如何处理?10分钟get了~
- 三 drf 认证,权限,限流,过滤,排序,分页,异常处理,接口文档,集xadmin的使用
因为接下来的功能中需要使用到登陆功能,所以我们使用django内置admin站点并创建一个管理员. python manage.py createsuperuser 创建管理员以后,访问admin站点 ...
- DRF之过滤排序分页异常处理
一.过滤 对于列表数据要通过字段来进行过滤,就需要添加 django-filter 模块 使用方法: # 1.注册,在app中注册 settings.py INSTALLED_APPS = [ 'dj ...
- drf07 过滤 排序 分页 异常处理 自动生成接口文档
4. 过滤Filtering 对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持. pip install django-filter 在配置文件sett ...
- day74:drf:drf其他功能:认证/权限/限流/过滤/排序/分页/异常处理&自动生成接口文档
目录 1.django-admin 2.认证:Authentication 3.权限:Permissions 4.限流:Throttling 5.过滤:Filtering 6.排序:OrderingF ...
- Django Rest Framework源码剖析(七)-----分页
一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...
- Django rest framework 基础
01: Django rest framework 基础 1.1 什么是RESTful 1. REST与技术无关,代表的是一种软件架构风格(REST是Representational Stat ...
- Django REST framework基础:视图和路由
DRF中的Request 在Django REST Framework中内置的Request类扩展了Django中的Request类,实现了很多方便的功能--如请求数据解析和认证等. 比如,区别于Dj ...
- Django Rest framework基础使用之 serializer
rest-framework文档地址:http://www.django-rest-framework.org/ Django Rest framework是一个非常强大且灵活的工具包,用于构建web ...
随机推荐
- input 弹起数字键盘的那些坑
input ios 踩的大坑 前言:最近有个需求要将全平台的交易密码由原来的 6-16位 复杂密码改为6位纯数字交易密码,涉及到非常多的业务场景,但修改起来也无非两种:设置交易密码,使用交易密码 设置 ...
- 微信小程序要求HTTPS,如何选择SSL证书?
为了保护小程序应用安全,微信官方的需求文档要求,每个微信小程序必须事先设置一个通讯域名,并通过HTTPS请求进行网络通信,不满足条件的域名和协议无法请求.因此开发者应先准备好配置好HTTPS证书的域名 ...
- Exchange 2013 清空邮箱
在某些应用场景中,需要清空用户邮箱的所有数据.如果使用Outlook web app或者Outlook 的邮件删除方式,对数以千计的邮件来说,实在不是一个好办法.exchange管理员可以使用&quo ...
- 新版vue作用域插槽的使用
2.6开始,作用域插槽的使用有了不同的地方: 作用域插槽的个人理解就是让子组件的数据可以在父组件中使用: 也是一个数据传递的方式了: 不多说,上代码 子组件定义一个插槽,并且定义一个需要传递到父组件 ...
- IDM调用参数
cmd调用IDM 基本命令: IDMan /d URL [/p 本地文件路径] [/f 本地文件名] [/q] [/h] [/n] [/a] 参数: 名称 作用 /d 下载文件的url /s 开始任务 ...
- numpy教程01---ndarray的创建
欢迎关注公众号[Python开发实战], 获取更多内容! 工具-numpy numpy是使用Python进行数据科学的基础库.numpy以一个强大的N维数组对象为中心,它还包含有用的线性代数,傅里叶变 ...
- 自家APP打开微信小程序,可行吗?
小程序的通用解决方案,今天为大家介绍一下FinClip.它的最大特点,就是能够让任何 App 运行小程序. 只需要在你的 App 里面,引入它的 SDK,就能加载运行外部小程序了.除了 SDK,它还提 ...
- Django中间件、csrf跨站请求、csrf装饰器、基于django中间件学习编程思想
django中间件 中间件介绍 什么是中间件? 官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子.它是一个轻量.低级别的插件系统,用于在全局范围内改变Django的输入和输出. ...
- jsp中c:forEach使用
首先需要在jsp中引入<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> ...
- Python学习笔记.md
Python学习笔记 1.变量类型 x=5 int x="ss" string x='a' string x=True bool #查看变量类型 type(x) 2.字符串常用操作 ...