首页 > 技术文章 > Day86--drf07--过滤排序和分页源码分析、JWT介绍与base64编码

Edmondhui 2021-12-14 00:05 原文

昨日回顾

1 三大认证源码分析
    -认证源码:
    APIView的dispatch----》initial---》perform_authentication
    ---》request.user-->Request类中找user方法---》Request的 _authenticate()

2 全局异常处理
    -统一返回格式
    -记录日志
    -使用步骤
    	-写一个函数,有两个参数
        -只要出异常,就会走这个函数
        -执行了一下原来的异常处理,处理了drf的异常(捕获了APIExcepiton的错误)
        -drf处理不了的我们自己处理了
        -返回Response对象
	-在配置文件中配置
        
3 接口文档
    -接口文档平台
    -第三方平台(自己搭,直接花钱使用第三方)
    -md,word写
    -自动生成
    	-swagger,coreapi
        
    -联调 (前后端联合调试)

今日内容

1.过滤,分页源码分析

1.1 过滤源码

1 查询所有才涉及到过滤(视图类必须继承:ListModelMixin+GenericAPIView)
2 在视图类中配置:filter_backends=[过滤类]
3 配置过滤字段:
    search_fields=['字段',]   #  内置过滤类:SearchFilter
    filterset_fields=['name','price']  # 第三方过滤类:DjangoFilterBackend

4 原理就是:执行过滤类中的:filter_queryset方法,在方法中完成过滤,排序
5 查询所有---》执行的是视图类的get方法--->就是ListModelMixin的list()

6 源码流程:
    6.1 ListModelMixin的list方法
	queryset = self.filter_queryset(self.get_queryset())  # 执行过滤
    
    6.2 视图类GenericAPIView的:self.filter_queryset()
	def filter_queryset(self, queryset):
            for backend in list(self.filter_backends):
            # 执行过滤类的filter_queryset方法完成过滤
            queryset = backend().filter_queryset(self.request, queryset, self)
            return queryset

1.2 分页源码

1 查询所有才涉及到分页(视图类必须继承:ListModelMixin+GenericAPIView)
2 在视图类中配置:pagination_class = 分页类  # 单个分类,就不用列表
3 查询所有---》执行的是视图类的get方法--->就是ListModelMixin的list()

4 源码流程:
    4.1 ListModelMixin的list方法
        page = self.paginate_queryset(queryset) # 1.执行分页
        if page is not None: # 如果使用了分页
            serializer = self.get_serializer(page, many=True)  # 2.只序列化当前分页的数据
            return self.get_paginated_response(serializer.data)  # 3.返回序列化后 的数据 (含有 上一页和下一页和总条数 的格式)

    4.2 视图类GenericAPIView中:self.paginate_queryset(queryset) 
	# self.paginator是在视图类中配置的分页类的对象 或者 为空	
	# 实现了分页功能,取出从前端地址中传入的,第几页,取多少条
        # 在该方法中自动实现分页,返回当前页码的数据
        return self.paginator.paginate_queryset(queryset, self.request, view=self)
	
    4.3 视图类GenericAPIView中:get_paginated_response(serializer.data)
        # self.paginator就是在视图类中配置的分页类的对象 或者 为空	
        return self.paginator.get_paginated_response(data)
        
    4.4 内置分页类PageNumberPagination的:get_paginated_response()
        def get_paginated_response(self, data):
            return Response(OrderedDict([
                ('count', self.page.paginator.count),
                ('next', self.get_next_link()),
                ('previous', self.get_previous_link()),
                ('results', data)
            ]))
 

1.3 自定义实现:继承APIView的视图,实现分页功能

# 原理:根据rest_framework的分页源码思路+自定义分页类
    
# page.py    
from rest_framework.pagination import  PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
    # 重写4个类属性即可
    page_size = 2 
    page_query_param = 'page'  
    page_size_query_param = 'size'  
    max_page_size = 5


# view.py:
class BookView(APIView):
    def get(self, request):
        # 1.先获取所有需要序列化的对象
        qs = Book.objects.all()
        
        # 2.分页,通过分页类分页
        # 2.1 实例化得到分页类对象,不需要传参数
        page = MyPageNumberPagination()  
        # 2.2 对qs进行分页,分页类对象page的某个方法来实现对qs分页
        res = page.paginate_queryset(qs, request, view=self)  # 返回的res是列表,是当前页码的所有数据
        
        # 3.对当前页码的数据,进行序列化
        ser = BookSerializer(instance=res, many=True)

        # 4.返回分页后的数据
        # return Response(ser.data)
        return page.get_paginated_response(ser.data)  # 使用过滤类的返回方法,带有上/下一页、总页的格式

2.前后端混合开发的后台管理模板

# 就是全栈开发-后台管理系统的前端系列页面模板(带有系列的Django模板语法)   就是纯前端页面 一堆的HTML、css和js文件

基于bootstrap框架的后台管理模板:  
    Admin LTE:https://adminlte.io/
        
基于layui框架的后台管理模板:  
    x-admin:http://x.xuebingsi.com/x-admin/v2.2/  # 已经停止维护,但可以使用
              
# 使用
    下载--解压--配置在自己的项目上

3.JWT介绍(****

# https://www.cnblogs.com/liuqingzheng/articles/8990027.html

# Json web token (JWT)
  是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).
  该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。
  JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,
  以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,
  该token也可直接被用于认证,也可被加密。

# JWT的构成
    -三段式:
    	-header:头部  一般放公司信息,加密方式 (用处不大)
        -payload:载荷  用户信息: user_id,expire(过期时间),user_name,email
        -signature:签名  第一部分和第二部加密得到的
        
    -通过base64转码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

3.1 drf项目的jwt认证开发流程(重点)

1.用账号密码访问登录接口,登录接口逻辑中调用 签发token算法得到token,
  返回给客户端,客户端自己存到cookies中

2.校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,
  所有视图类请求都会进行认证校验,所以请求带了token,就会反解出user对象,
  在视图类中用request.user就能访问登录的用户

注:登录接口需要做 认证 + 权限 两个局部禁用

3.2 JWT快速使用(内置的签发与认证)

# base64编码 (跟语言无关,任何语言都能编码、解码base64)

base64的编码长度都是4的倍数,若是解码出错,字符串的长度不足4的倍数时,
就需要用'='补齐,最多三个等号

import base64
    -.b64encode(str)  # 转码 str是bytes类型的字符串 (json字符串)
    -.b64decode(str)  # 解码 
    
# 网络上的有些图片,就是采用base64编码	
# 使用第三方模块:django-rest-framework-jwt(作者好多年不改了)
# 新的第三方模块,用起来跟它差不多(名字忘了)

# 安装:pip install djangorestframework-jwt    

# 快速使用,签发token给前端
    -用户表使用auth的user表
    -登录功能不用写,djangorestframework-jwt帮你写好了

签发

# 在路由中配置
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    path('login/', obtain_jwt_token),  # 登录接口好了,可以签发token,依赖了auth的user表
]

认证

from rest_framework_jwt.authentication import JSONWebTokenAuthentication  # drf_jwt内置的认证类
from rest_framework.permissions import IsAuthenticated 
class BookView(APIView):
    # 使用drf_jwt内置的认证,必须加这两个
    authentication_classes = [JSONWebTokenAuthentication, ]
    permission_classes = [IsAuthenticated]
        
# 使用时,需要在请求头添加(模块的固定写法)
authorization ='jwt token值'   # 'jwt' + 空格 + token值     authorization n.授权

作业

# 1 基于APIViwe实现book的分页,偏移分页

# 自定义分页类:偏移分页
class OffsetPagination(LimitOffsetPagination):
    default_limit = 2
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 10

class BookView(APIView):
    def get(self, request):
        # 1.查出所需序列化的book对象
        queryset = models.Book.objects.all()
        # 2.用自定义的分页类 分页
        offset_obj = OffsetPagination()
        page = offset_obj.paginate_queryset(queryset=queryset, request=request, view=self)
        # 3.序列化当前页的数据
        serializer = BookModelSerializer(page, many=True)
        # 4. 返回序列化的数据
        return offset_obj.get_paginated_response(serializer.data)


# 2 使用jwt的快速签发和认证(登录后才能访问)

# 3 作业:
    1 自定义User表,新增mobile唯一约束字段;新增icon图片字段
    2 在自定义User表基础上,用 GenericViewSet + CreateModelMixin + serializer 完成User表新增接口(就是注册接口)(重要提示:序列化类要重写create方法,不然密码就是明文了)
    3 在自定义User表基础上,用 GenericViewSet + RetrieveModelMixin + serializer 完成User表单查(就是用户中心)
    4 在自定义User表基础上,用 GenericViewSet + UpdateModelMixin + serializer 完成用户头像的修改
    
    见 day86/drf_User

# 4 什么是集群,什么是分布式
集群:同一个业务,部署在多个服务器上(不同的服务器运行同样的代码,干同一件事)
分布式:一个业务分拆多个子业务,部署在不同的服务器上(不同的服务器,运行不同的代码,为了同一个目的)

将一套系统拆分成不同子系统部署在不同服务器上(这叫分布式),
然后部署多个相同的子系统在不同的服务器上(这叫集群),
部署在不同服务器上的同一个子系统应做负载均衡。

django 3.0之前是同步,3.0之后是异步

# 5 部分人:读obtain_jwt_token的源码
# 6 部分人:JSONWebTokenAuthentication源码

推荐阅读