12_07、过滤、排序、分页、异常

一、过滤

1、内置的过滤类

思路

复制代码
# 内置的过滤类(模糊查询)

### 第一步:导入
from rest_framework.filters import SearchFilter

### 第二步:在视图类中写
# 在视图类中
# 必须继承GenericAPIView,才有这个类属性
filter_backends = [SearchFilter,]
# 需要配合一个类属性,可以按name过滤
search_fields=['name','author']

### 第三步:搜索的时候,模糊搜索
http://127.0.0.1:8000/books/?search=红
http://127.0.0.1:8000/books/?search=清  # 书名或者author中带清就能搜到
复制代码

模型

复制代码
from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    author = models.CharField(max_length=32)
View Code
复制代码

序列化器

复制代码
from app01 import models
from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
View Code
复制代码

路由

复制代码
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)),
]
View Code
复制代码

视图接口

复制代码
from django.shortcuts import render
from rest_framework.generics import ListAPIView, GenericAPIView  # 九个视图子类
from app01.serializer import BookSerializer
from app01.models import Book
from rest_framework.viewsets import ViewSetMixin, GenericViewSet
from rest_framework.mixins import ListModelMixin  # 五个视图扩展类,配合GenericAPIView使用
from rest_framework.filters import SearchFilter


# class BookView(ViewSetMixin, ListAPIView):  # 查询所有接口(路由,九个子类之一)
class BookView(GenericViewSet, ListModelMixin):  # 查询所有接口(子路由,五个视图扩展类,需要配合GenericAPIView)
    queryset = Book.objects.all()  # 获取所有
    serializer_class = BookSerializer  # 序列化

    filter_backends = [SearchFilter, ]
    # search_fields = ['title']  # 按书籍名称模糊查询
    search_fields = ['title', 'author']  # 按书籍名称、作者模糊查询
View Code
复制代码

结果

 

 

2、第三方过滤器

思路

复制代码
# 第三方过滤类(精准查询)

### 第0步:安装
pip3 install django-filter

### 第一步:注册
INSTALLED_APPS = [
        。。。
    'django_filters',
]

### 第二步:导入过滤类
from django_filters.rest_framework import DjangoFilterBackend

### 第三步:在视图类中使用
class BookView(GenericViewSet,ListModelMixin):
    # 必须继承GenericAPIView,才有这个类属性
    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条件
http://127.0.0.1:8000/books/?author=刘清政
复制代码

注册

INSTALLED_APPS = [
    'django_filters'  # 第三方过滤需要注册
]

视图接口

复制代码
from app01.serializer import BookSerializer
from app01.models import Book
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin  # 五个视图扩展类,配合GenericAPIView使用
from django_filters.rest_framework import DjangoFilterBackend


class BookView(GenericViewSet, ListModelMixin):  # 查询所有接口(子路由,五个视图扩展类,需要配合GenericAPIView)
    queryset = Book.objects.all()  # 获取所有
    serializer_class = BookSerializer  # 序列化

    filter_backends = [DjangoFilterBackend, ]  # 第三方参数改变
    filter_fields = ['title', 'author']  # 按书籍名称、作者模糊查询
复制代码

结果

 

3、自定义过滤器

思路

复制代码
# 自定义过滤类(模糊查询)

### 第一步:写一个类,继承BaseFilterBackend 基类,重写filter_queryset方法,返回qs对象,是过滤后的对象
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):
    #filter_backends = [BookNameFilter,]
    filter_backends = BookNameFilter
    
### 第三步:查询方式
http://127.0.0.1:8000/books/?name=红  # 模糊匹配 ,自己定义的
复制代码

过滤器

复制代码
from rest_framework.filters import BaseFilterBackend

class BookNameFilter(BaseFilterBackend):  # 写一个类,继承BaseFilterBackend
    # 重写filter_queryset方法,让返回的qs对象,是过滤后的对象
    def filter_queryset(self, request, queryset, view):
        query = request.query_params.get('title')
        if query:
            queryset = queryset.filter(title__contains=query)
        return queryset
复制代码

视图接口

from .filter import BookNameFilter

class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()  # 1,获取所有,赋值给queryset
    serializer_class = BookSerializer  # 2,利用序列化器,json字段
    # filter_backends = BookNameFilter  # 不能这样写,type必须是一个可迭代对象
    filter_backends = [BookNameFilter, ]  # 3,利用过滤器

结果

 

 

 自定义过滤器的源码解析:为什么这样写就能过滤,为什么这样写就能调用

# 源码分析----》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

过滤器与get请求参数的异同

二、排序

思路

复制代码
# 按照id排序,按照年龄排序,按照价格排序
# 使用内置的即可

## 第一步:导入内置排序类
from rest_framework.filters import OrderingFilter

## 第二步:在视图类中配置(必须继承GenericAPIView)
class BookView(GenericViewSet,ListModelMixin):
    # 支持按price排序
    filter_backends = [OrderingFilter,]
    ordering_fields=['price']

## 第三步:查询
http://127.0.0.1:8000/books/?ordering=-price  # 倒序排
http://127.0.0.1:8000/books/?ordering=-price,-id # 先按价格倒序排,如果价格一样,再按id倒序排
    
    
# 过滤和排序可以同时用--->因为他们本质是for循环一个个执行,所有优先使用过滤,再使用排序
filter_backends = [SearchFilter,OrderingFilter]
ordering_fields=['price','id']
search_fields=['name','author']
View Code
复制代码

视图接口

from rest_framework.filters import OrderingFilter

class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()  # 1,获取所有,赋值给queryset
    serializer_class = BookSerializer  # 2,利用序列化器,json字段

    filter_backends = [OrderingFilter, ]  # 3,实例化排序对象
    Ordering_fields = ['price']  # 4,按价格排序

结果

三、分页

在五个接口中,注意分页的使用,只有在查询所有这个接口中,才有涉及

pc端,分页是上一页、下一页的展现形式

app和小程序中,分页是下拉,上拉的展现形式

这里提供三种分页方法

第一种:基本分页

思路

复制代码
# PageNumberPagination:基本分页--》按照页码数,每页显示多少条
#第一步:写一个类,继承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条

#第二步:配置在视图类上,必须继承GenericAPIView才有
pagination_class = PageNumberPagination

# 第三步:查询方式
http://127.0.0.1:8000/books/?page=2&size=8
View Code
复制代码

分页类

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class CommonPageNumberPagination(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 .page import CommonPageNumberPagination as PageNumberPagination

class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()  # 1,获取所有,赋值给queryset
    serializer_class = BookSerializer  # 2,利用序列化器,json字段

    pagination_class = PageNumberPagination

结果

第二种:偏移分页

 思路

复制代码
# LimitOffsetPagination---》偏移分页

#第一步:写一个类,继承LimitOffsetPagination,重写四个类属性
    default_limit = 2  # 默认一页获取条数 2  条
    limit_query_param = 'limit'  # ?limit=3  获取三条,如果不传,就用上面的默认两条
    offset_query_param = 'offset'  # ?limit=3&offset=2  从第2条开始,获取3条    ?offset=3:从第三条开始,获取2条
    max_limit = 5  # 最大显示条数 5 条

#第二步:配置在视图类上,必须继承GenericAPIView才有
pagination_class = CommonLimitOffsetPagination

# 第三步:查询方式
http://127.0.0.1:8000/books/?limit=2&offset=1
View Code
复制代码

 分页类

# 偏移分页
from rest_framework.pagination import LimitOffsetPagination

class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2  # 默认一页获取条数 2  条
    limit_query_param = 'limit'  # ?limit=3  获取三条,如果不传,就用上面的默认两条
    offset_query_param = 'offset'  # ?limit=3&offset=2  从第2条开始,获取3条    ?offset=3:从第三条开始,获取2条
    max_limit = 5  # 最大显示条数 5 条

视图类

from .page import CommonLimitOffsetPagination

class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()  # 1,获取所有,赋值给queryset
    serializer_class = BookSerializer  # 2,利用序列化器,json字段

    # pagination_class = PageNumberPagination
    pagination_class = CommonLimitOffsetPagination

结果

第三种:游标分页

 思路

复制代码
# CursorPagination---》游标分页

#第一步:写一个类,继承CursorPagination,重写四个类属性
class CommonCursorPagination(CursorPagination):
    page_size = 2  # 每页显示2条
    cursor_query_param = 'cursor'  # 查询条件  ?cursor=sdafdase
    ordering = 'id'   # 排序规则,使用id排序

#第二步:配置在视图类上,必须继承GenericAPIView才有
pagination_class = CommonCursorPagination

# 第三步:查询方式
http://127.0.0.1:8000/books/?cursor=cD02
View Code
复制代码

 分页类

from rest_framework.pagination import CursorPagination

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

视图接口

from .page import CommonCursorPagination

class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()  # 1,获取所有,赋值给queryset
    serializer_class = BookSerializer  # 2,利用序列化器,json字段

    pagination_class = CommonCursorPagination

游标分页跟其他两种分页的区别

#### 注意:
# 跟上面两种的区别:上面两种,可以从中间位置获取某一页,Cursor方式只能上一页和下一页
# 上面这两种在获取某一页的时候,都需要从开始过滤到要取的页面数的数据
# 下面这种方式,先排序,内部维护了一个游标,游标只能选择往前走或往后走,在取某一页的时候,不需要过滤之前的数据
# 这种分页方式特殊,只能选择上一页和下一页,不能指定某一页,但是速度快,适合大数据量的分页
# 大数据量和app分页---》下拉加载下一页,不需要指定跳转到第几页

 

四、异常

思路

复制代码
# 之前读APIViwe源码的时候,捕获了全局异常,在执行三大认证,视图类的方法时候,如果出了异常,会被全局异常捕获

# 统一返回格式,无论是否异常,返回的格式统一  ,记录日志(好排查)
    {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):
    # 第一步,先执行原来的exception_handler
    # 第一种情况,返回Response对象,这表示已经处理了异常,它只处理APIExcepiton的异常,第二种情况,返回None,表示没有处理
    res = exception_handler(exc, context)
    if res:  # exception_handler 已经处理了,暂时先不处理了
        # 998:APIExcepiton
        # res=Response(data={'code':998,'msg':'服务器异常,请联系系统管理员'})
        res = Response(data={'code': 998, 'msg': res.data.get('detail', '服务器异常,请联系系统管理员')})
    else:
        # 999:出了APIExcepiton外的异常
        # res=Response(data={'code':999,'msg':'服务器异常,请联系系统管理员'})
        res = Response(data={'code': 999, 'msg': str(exc)})

    # 注意:咱们在这里,可以记录日志---》只要走到这,说明程序报错了,记录日志,以后查日志---》尽量详细
    # 出错时间,错误原因,哪个视图类出了错,什么请求地址,什么请求方式出了错
    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': 'app01.exception.common_exception_handler' # 再出异常,会执行这个函数
}
    
    
### 以后再出异常,都会走这个函数,后期需要记录日志,统一了返回格式
View Code
复制代码

 

posted @   三三得九86  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示