在之前所写的五个接口中,只有获取所有需要过滤,其他接口都不需要。如在访问的时候带参数过滤出自己想要的数据。
http://127.0.0.1:8080/?search=活着
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=华
使用第三方过滤类,
第一步先安装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是余华的
单独写一个类继承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=西 # 模糊匹配 ,自己定义的
可以使用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']
接口中也只有查询所有用到了分页。
默认的三种分页方法
基本分页,按照页码数,每页显示多少条
单独创建一个文件专门用来分页: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条
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/
从第二条开始,每页显示三条
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/
使用这三种分页视图类上,必须继承GenericAPIView 前面两种可以从中间位置获取某一页,但是游标分页方式只能上一页和下一页 前面两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据 游标分页方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页,在大数据量和app分页时,下拉加载下一页,不需要指定跳转到第几页
DRF中捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获。
如果自己要自己处理异常,则需要考虑:统一返回格式,无论是否异常,返回的格式统一 ,记录日志(好排查)
异常: {code:999,msg:服务器异常,请联系系统管理员} 成功: {code:100,msg:成功,data:[{},{}]}
写一个视图函数
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')
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