过滤、排序、分页、异常处理
上期内容回顾
不需要显示继承一个类,只要多个类中有同样的属性或方法,我们把它们称之为一种类,python,go
如果要属于同一类,必须显示的继承某个基类,这样才属于基类这个类型,java
要么认为约定必须有哪些方法(符合鸭子类型),可控性低;
要么强制约定有哪些方法(abc模块,使用抛异常)
重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变
重载:是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
写一个类,继承BaseAuthentication,重写authenticate,在方法中校验,如果登录了,返回两个值,如果没登陆,抛出异常
全局使用
局部使用
局部禁用
写一个类,继承BasePermission,重写has_permission方法,在方法中判断,如果有权限,返回True ,如果没有权限,返回false
全局使用
局部使用
局部禁用
写一个类,继承SimpleRateThrottle
重写get_cache_key,返回什么就以什么做频率限制
重写类属性scope = 'minute_3' ,在配置文件中配置:'DEFAULT_THROTTLE_RATES' : {'minute_3' : '3/m' }
全局使用
局部使用
局部禁用
今日内容概要
内容详细
1、过滤
内置的过滤类
第三方过滤类
自定义过滤类
from rest_framework.filters import SearchFilter
filter_backends = [SearchFilter, ]
search_fields = ['name' , 'author' ]
//127.0 .0 .1 :8000 /books/?search=火
//127.0 .0 .1 :8000 /books/?search=田
pip3 install django-filter
INSTALLED_APPS = [
'django_filters' ,
]
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' ]
//127.0 .0 .1 :8000 /books/?name=火影忍者
//127.0 .0 .1 :8000 /books/?name=海贼王&author=尾田
//127.0 .0 .1 :8000 /books/?author=尾田
from rest_framework.filters import BaseFilterBackend
class BookNameFilter (BaseFilterBackend) :
def filter_queryset (self, request, queryset, view) :
query = request.query_params.get('name' )
if query:
queryset = queryset.filter(name__contains=query)
return queryset
from .filter import BookNameFilter
class BookView (GenericViewSet, ListModelMixin) :
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [BookNameFilter, ]
//127.0 .0 .1 :8000 /books/?name=忍
"""
### 源码分析: GenericAPIView----》查询所有,调用了list---》self.filter_queryset(self.get_queryset())----》查看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
"""
数据准备:models.py:
from django.db import models
class Book (models.Model) :
name = models.CharField(max_length=32 )
price = models.IntegerField()
author = models.CharField(max_length=32 )
新建:serializer.py:
from .models import Book
from rest_framework import serializers
class BookSerializer (serializers.ModelSerializer) :
class Meta :
model = Book
fields = '__all__'
新建 filter.py:
from rest_framework.filters import BaseFilterBackend
class BookNameFilter (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 .models import Book
from .serializer import BookSerializer
from rest_framework.generics import ListAPIView
from rest_framework.viewsets import ViewSetMixin, GenericViewSet
from rest_framework.filters import SearchFilter
from rest_framework.mixins import ListModelMixin
from django_filters.rest_framework import DjangoFilterBackend
from .filter import BookNameFilter
class BookView (GenericViewSet, ListModelMixin) :
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [BookNameFilter, ]
路由修改 urls.py:
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
router.register('books' , views.BookView, 'books' )
urlpatterns = [
path('admin/' , admin.site.urls),
path('' , include(router.urls)),
]
2、排序
from rest_framework.filters import OrderingFilter
class BookView (GenericViewSet, ListModelMixin) :
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter, ]
ordering_fields = ['price' , 'id' ]
//127.0 .0 .1 :8000 /books/?ordering=price
//127.0 .0 .1 :8000 /books/?ordering=-price
//127.0 .0 .1 :8000 /books/?ordering=-price,-id
"""
### 过滤和排序可以同时用:
因为他们本质是for循环一个个执行,所有优先使用过滤,再使用排序
filter_backends = [SearchFilter, OrderingFilter]
ordering_fields=['price', 'id']
search_fields=['name', 'author']
"""
3、分页
PageNumberPagination
LimitOffsetPagination
CursorPagination
page_size = 3
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 5
pagination_class = PageNumberPagination
//127.0 .0 .1 :8000 /books/?page=2
//127.0 .0 .1 :8000 /books/?page=2 &size=4
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
pagination_class = CommonLimitOffsetPagination
//127.0 .0 .1 :8000 /books/?limit=2 &offset=3
page_size = 2
cursor_query_param = 'cursor'
ordering = 'id'
pagination_class = CommonCursorPagination
//127.0 .0 .1 :8000 /books/?cursor=cD02
新建 page.py:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class CommonPageNumberPagination (PageNumberPagination) :
page_size = 3
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 5
class CommonLimitOffsetPagination (LimitOffsetPagination) :
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
class CommonCursorPagination (CursorPagination) :
page_size = 2
cursor_query_param = 'cursor'
ordering = 'id'
视图类 views.py中:
"""
分页
"""
from .page import CommonPageNumberPagination as PageNumberPagination
from .page import CommonLimitOffsetPagination, CommonCursorPagination
"""
跟上面两种的区别:上面两种,可以从中间位置获取某一页,Cursor方式只能上一页和下一页
上面这两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据
下面这种方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据
这种分页方式特殊,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页
适用于:大数据量和app分页---》下拉加载下一页,不需要指定跳转到第几页
"""
class BookView (GenericViewSet, ListModelMixin) :
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter]
search_fields = ['name' , 'author' ]
pagination_class = CommonCursorPagination
4、异常处理
在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获
{code:999 ,msg:服务器异常,请联系系统管理员}
{code:100 ,msg:成功,data:[{},{}]}
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler (exc, context) :
res = exception_handler(exc, context)
if res:
res = Response(data={'code' : 998 , 'msg' : res.data.get('detail' , '服务器异常,请联系系统管理员' )})
else :
res = Response(data={'code' : 999 , 'msg' : str(exc)})
request = context.get('request' )
view = context.get('view' )
print('错误原因:%s,错误视图类:%s,请求地址:%s,请求方式:%s' % (str(exc), str(view), request.path, request.method))
return res
REST_FRAMEWORK = {
'EXCEPTION_HANDLER' : 'app01.exception.common_exception_handler'
}
视图类 views.py:
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 Exception('我错了' )
return Response('ok' )
配置测试路由 urls.py:
path('test/' , views.TestView.as_view()),