Python django-rest-framework 过滤、排序、分页、异常处理

Python 投稿 68300 0 评论

Python django-rest-framework 过滤、排序、分页、异常处理

django-rest-framework 基础四 过滤、排序、分页、异常处理

1. 过滤

http://127.0.0.1:8080/?search=活着

1.1 内置过滤类

    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']

路由

    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 第三方过滤类

第一步先安装

    pip install django-filter

第二步在配置里注册

    INSTALLED_APPS = [
	...
    'rest_framework',
    'django_filters',
]

第三步在视图类中使用

    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']

路由还是原来的配置。

http://127.0.0.1:8000/books/?name=西游记, 如果像之前直接写西则匹配不出来

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

1.3 自定义过滤类

    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
    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,]

由于只写了字段,所以只能匹配

    http://127.0.0.1:8000/books/?name=西  # 模糊匹配 ,自己定义的

2. 排序

    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  # 倒序, 使用减号(-)为倒序
    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

单独创建一个文件专门用来分页:

    # 继承 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条
    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

路由不变,默认访问:

可看到默认显示3条,

3.2 方法二:偏移分页 LimitOffsetPagination

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 条
    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

访问:

3.3 方法三 游标分页 CursorPagination

from rest_framework.pagination import CursorPagination

class commCursorPagination(CursorPagination):
    page_size = 3  # 每页显示2条
    cursor_query_param = 'cursor'   # 查询条件  ?cursor=sdafdase
    ordering = 'id' # 排序规则,使用id排序

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

访问:

3.4 三种分页总结

使用这三种分页视图类上,必须继承GenericAPIView 前面两种可以从中间位置获取某一页,但是游标分页方式只能上一页和下一页 前面两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据 游标分页方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页,在大数据量和app分页时,下拉加载下一页,不需要指定跳转到第几页

4. 异常处理

DRF中捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获。

异常:
{code:999,msg:服务器异常,请联系系统管理员}
成功:
{code:100,msg:成功,data:[{},{}]}

4.1 自己处理异常

写一个视图函数

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')

配置路由

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()),
]

使用自带的异常处理时:

第一步:创建一个专门处理的文件,里面写处理异常的代码。

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
  
  

    
### 以后再出异常,都会走这个函数,后期需要记录日志,统一了返回格式

第二步:把函数配置在配置文件中

REST_FRAMEWORK = {     'EXCEPTION_HANDLER':  'authenticated.exceptions.common_exception_handler',# 再出异常,会执行这个函数 } # authenticated 为应用名

第三步:测试

    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')
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')

访问发现这个里面就没有,因为这是一个异常,DRF会捕捉到。

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 出现异常记录日志

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的日志模块

编程笔记 » Python django-rest-framework 过滤、排序、分页、异常处理

赞同 (81) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽