drf- 过滤、排序、异常处理

发布时间 2023-09-10 10:27:48作者: Way*yy

session的执行流程

写一个登录接口----->保存用户的登录状态
	-获取到用户名,密码
    -使用request.session["username"] = 用户名、或者request.session["pk"] = pk值
    -签发阶段做了三件事:
    	-1、生成一个随机的字符串
        -2、在django_session表中保存
        	-session_key:随机字符串
            -session_data:加密后的数据
            -expire_data:截止到14天后到期自动删除
        -3、把随机的字符串以cookie形式返回给前端(存在浏览器的cookie中)
    -认证阶段:
    	-1、取出在cookie中session_id对应的随机字符串
        -2、通过这个随机字符串在django_session表中查询,如果查询的到说明该用户是在登录状态
        -3、将session_data的数据放到request_session中

过滤

只针对于,查询所有接口,必须要继承 GenericAPIView

安装:
    -pip install django==3.2.12
    -pip install django-filter

使用方法

方法一:内置的、模糊匹配
from rest_framework.filters import SearchFilter
    # 重写属性
    filter_backends = [SearchFilter, OrderingFilter]
    search_fields = ['name', 'price']
    # 查询方式
    http://127.0.0.1:8000/text/?search=水 # 就可以查出来与水相关的书籍
            
方式二:第三方的、精准匹配而且支持"&"
from django_filters.rest_framework import DjangoFilterBackend
	# 重写属性
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["name", "price"]
    # 查询方式:
    http://127.0.0.1:8000/text/?name=红楼梦 or
    http://127.0.0.1:8000/text/?name=红楼梦&price=123
            
方式三:自定义方法
    # 重写属性
    filter_backends = [BookFilter]
    # 查询方式
	http://127.0.0.1:8000/books/?price=123&name=西游记

自定义过滤方法

from rest_framework.filters import BaseFilterBackend


class BookFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        search_parm = request.query_params.get("name")
        search_price = request.query_params.get("price")
        print("++++", search_parm, "-----", search_price)
        if search_parm and search_price:
            print("走到这里了")
            queryset = queryset.filter(name__contains=search_parm, price=search_price)
            return queryset

继承ViewSet实现的视图

class BookText(ViewSet):
    def list(self, request):
        # 获取到前端传入的筛选条件
        search_parm = request.query_params.get("name")
        # 通过__contains找到与我这个name字段是否有我search_parm的值,如果有返回这个值的对象
        book_obj = Book.objects.all().filter(name__contains=search_parm)

        serializers = BookSerializers(instance=book_obj, many=True)

        return Response(serializers.data)

继承GenericAPIView写过滤类,可以写多个

# 写多个,他们是从左往右,依次执行
# 大原则,配置多个过滤类的时候,第一个放尽量多个过滤掉数据
# 配置多个:执行原理
	-先执行第一个过滤类的:filter_queryset返回qs对象
    -再执行第二个过滤类的filter_queryset,传入上一个返回的qs,过滤完返回qs对象

过滤小结

1、内置的:from rest_framework.filters import SearchFilter
	-重写属性:
    	-filter_backends、search_fields
2、第三方的:from django_filters.rest_framework import DjangoFilterBackend
	-重写属性:
    	-filter_backends、filterset_fields
3、自定义的:from rest_framework.filters import BaseFilterBackend
	-自己写一个类继承BaseFilterBackend,可以按照自己的想法,指定想要过滤的字段
    -重写属性:
    	-filter_backends

分类

只针对于查询所有接口:
	-分页展示形式
    	web:上一页,下一页
        小程序:上拉加载更多
        
必须继承 GenericAPIView

三种分页方式------->drf提供的
导入模块:
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

使用PageNumberPagination分页类

class ComePageNumber(PageNumberPagination):
    # 重写参数
    page_size = 3  # 每页展示3条
    page_query_parm = "page"  # 索引前缀
    page_size_query_param = "size"  # # 可以指定每页显示多少条 eg:size=300000
    max_page_size = 4

前端展示

{
    "count": 4, # 总数据量
    "next": "http://127.0.0.1:8000/text/?page=2", # 下一页
    "previous": null, # 前一页
    "results": [
        {
            "id": 1,
            "name": "西游记",
            "price": "123"
        },
        {
            "id": 2,
            "name": "红楼梦",
            "price": "234"
        },
        {
            "id": 3,
            "name": "红楼梦",
            "price": "345"
        }
    ]
}

使用LimitOffsetPagination分页类

class ComeLimit(LimitOffsetPagination):
    default_limit = 2  # 默认每页显示2条
    limit_query_param = "limit"  # 每页显示多少条的查询
    offset_query_param = "offset"  # 从第几条数据开始
    max_limit = 5  # limit最多取5条

前端展示

URLs:
	http://127.0.0.1:8000/text/?limit=3&offset=3&size=2
{
    "count": 4,
    "next": null,
    "previous": "http://127.0.0.1:8000/text/?limit=3&size=2",
    "results": [
        {
            "id": 4,
            "name": "水浒传",
            "price": "456"
        }
    ]
}

使用CursorPagination分页类

class ComeCursor(CursorPagination):
    cursor_query_param = "cursor"
    page_size = 2
    ordering = "id"

前端展示

URLs:
	http://127.0.0.1:8000/text/?cursor=cj0xJnA9Mw%3D%3D
{
    "next": "http://127.0.0.1:8000/text/?cursor=cD0y",
    "previous": null,
    "results": [
        {
            "id": 1,
            "name": "西游记",
            "price": "123"
        },
        {
            "id": 2,
            "name": "红楼梦",
            "price": "234"
        }
    ]
}

分页类总结

继承PageNumberPagination分页类:
	-重写属性:
        -page_size = 4 # 每页显示4条
        -page_query_parm = "page"  # 索引前缀
        -page_size_query_param = "size" # 可以指定每页显示多少条 eg:size=300
        -max_page_size = 4

继承LimitOffsetPagination分页类:
	-重写属性:
        -default_limit = 2  # 默认每页显示2条
        -limit_query_param = "limit"  # 每页显示多少条的查询
        -offset_query_param = "offset"  # 从第几条数据开始
        -max_limit = 5  # limit最多取5条
        
继承CursorPagination分页类:
	-重写属性:
        -cursor_query_param = "cursor"
        -page_size = 2
        -ordering = "id"
        
PageNumberPagination适用于传统的分页需求,简单易用,但在大数据量和实时数据更新的场景下性能可能有限。

LimitOffsetPagination适用于快速定位到某个位置的场景,但在大数据量和实时数据更新的场景下性能可能有限。

CursorPagination适用于大数据量和实时数据更新的场景,可以灵活定制排序方式,但需要客户端保存和传递游标信息

继承GenericAPIView实现的视图

class Text(ViewSetMixin, ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializers
    ################分页类################
    pagination_class = ComeCursor 
    pagination_class = ComeLimit
    pagination_class = ComePageNumber
    # 三者选其一即可,前提是三个分页类都写好了
    #####################################
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ["name", "price"]

继承APIView实现


异常处理

# drf中的所有异常都会被全局捕捉到
	-认证类,认证不通过抛出异常,会被全局捕捉到,所以前端不会崩掉,只会得到错误信息
    
drf全局异常处理,他会把drf的异常处理掉,统一返回格式,但是django原生的和python的都不会处理

# 我们要做的就是无论是Python还是Django异常我们都可以捕捉到,统一格式返回

新建一个exception文件

1、写一个方法;
from rest_framework.views import exception_handler

from rest_framework.response import Response


    def my_exception(exc, context):
        exc = exception_handler(exc, context)  # 返回结果为None
        if not exc: # 如果不为None就除drf的异常
            return Response({'code': 1000, 'msg': '非drf错误,错误信息是:%s' % str(exc)})
        else: # 否则就是drf的异常
            return Response({'code': 1000, 'msg': 'drf错误,错误信息是:' + exc.data.get('detail')})

2 配置文件配置
    REST_FRAMEWORK = {
        'EXCEPTION_HANDLER': 'app01.exceptions.common_exception', # 以后只要出了异常,都会走这个函数
    }

补充

1 DateField、DateTimeField
    -auto_now_add=True    # 创建数据时自动添加当前时间
    -auto_now=True           # 每次操作数据时更新为当前时间

作业

# 1 DateTimeField的两个字段属性有什么用,如果没做国际化,存的时间什么样,查出来打印出来的时间是什么样的?(做过国际化和没做的区别)

# 2 继承APIView实现3种分页

page = self.paginate_queryset(queryset) # 使用所有数据分页
# paginate=CommonCursorPagination()
# page=paginate.paginate_queryset(queryset, self.request, view=self)
if page is not None:
     serializer = BookSerializer(page, many=True) #序列化分页后的数据
     return self.get_paginated_response(serializer.data)


# 3 三种方式实现过滤
# 4 继承GenericAPIView写三种分页
# 5 写一个全局异常处理,以后无论出什么错,都返回固定格式

DateField、DateTimeField的两个字段属性有什么用,如果没做国际化,存的时间什么样,查出来打印出来的时间是什么样的?(做过国际化和没做的区别)

DateField和DateTimeField是Django模型中用于存储日期和日期时间的字段属性。

DateField:用于存储日期,只包含年、月、日的信息。
DateTimeField:用于存储日期时间,包含年、月、日、时、分、秒的信息。
如果没有进行国际化处理,存储的时间会按照数据库的时区进行存储,通常是UTC时间(协调世界时)。当从数据库中查询出来并打印时,时间仍然会以UTC时间的格式显示。