DRF大回顾

发布时间 2023-05-31 21:43:58作者: 橘子熊何妨

drf大回顾

1 drf入门规范
	-前后端开发模式:混合,分离
    -API接口:地址(url),请求方法(method),请求参数(request),返回值(response)
    -postman的使用
    -序列化和反序列化
    -restful规范
    	-http响应状态码
        
    -想在django中写符合规范的接口
    -djangorestframework(drf):django的app
    -快速使用
    
2 CBV源码分析
	-路由中 视图类.as_viwe()--->django的View的as_viwe()---->执行结果 view内存地址----》请求来了,路由匹配成功----》view(request)---->return self.dispatch()--->View的dispatch---》通过反射,不同的请求会执行视图类中跟请求同名的方法
# 最终本质跟写fbv的执行流程一样
# 最终结论:什么请求方式,就会执行视图类中的什么方法


3 APIView执行流程
	# APIView 继承了View
	# 视图类:class BookView(APIView):
    # 路由中 视图类.as_viwe()--->drf的APIView的as_viwe()--->调用父类的as_view,去除csrf---》View类的as_view内部的view闭包函数(加了个装饰器)---》view(request)---->return self.dispatch()---》APIView的dispatch()
    
    
    # 以后视图类,只要继承APIView及其子类
   
    
    # 装饰器的本质原理
    @装饰器  # add=装饰器(add)
    def add()
    # 闭包函数

    
    
    
    
# 补充
	类中:self
    类中:super()
# 1 Request 类的对象 以后用的request的源码分析
	-request.data
    -request.query_params
    -request.Files
    -request.method,request.path....跟之前一样
    -重写了魔法方法---》__getattr__---->对象.属性,不存在会触发---》反射了self._request
    # 将老的request赋值给_request,然后包装一个新的request
    
# 2 序列化组件
	-能做的事:
    	1. 序列化,序列化器会把模型对象(queryset,单个对象)转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request.data以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
	-Serialzier---》book表的先做序列化,使用
        -定义序列化类
            class BookSerializer(Serialzier):
                name=serializer.CharField()
        -使用序列化类
            ser=BookSerializer(instance=qs,单个对象,many=True)
            ser.data  # 字典
     -字段类:很多
    	ListField
        DictField
     -字段参数:反序列化校验,字段自己的规则
    	read_only
        write_only
        
        
     -反序列化校验	
    	-字段自己
        -局部钩子:validate_字段名
        -全局钩子:validate
        
     -反序列化保存
    	-校验过后,ser.save()--->判断instance是否存在,如果不存在,就是调用ser的create方法
        -在序列化类中,重写create
        
     -反序列化更新
    	-校验过后,ser.save()--->判断instance是否存在,如果存在,就是调用ser的update方法
        -在序列化类中,重写update
        
        
        
    -ModelSerializer的使用---》不需要一个个写字段,跟表模型有对应关系
    	-extra_kwargs
        -其它跟Serializer一模一样
        
        
    -序列化,定制返回格式
    	-source
        -SerializerMethodField:写在序列化类中,配合一个  get_字段名的方法,返回什么,前端就看到什么
        -在表模型中写:写方法,返回字典,列表,字符串---》在序列化类中可以使用ListField,DictField
    	
    
#3 请求与响应
	Request类的对象
    	-接受前端传入的编码格式:json,urlencoded,form-data
            -局部配置
            -全局配置
         -Request的源码
    
    Response类的对象
    	-前端看到的形式(浏览器,json)
        -源码分析
        	-data
            -headers
            -status_code
    
    
# 4 视图组件
	-两个视图基类:APIView,GenericAPIView
    -GenericAPIView:
    -5 个视图扩展列,必须配合GenericAPIView
    	-{'code':100,'msg':'成功'}
        -基于它在封装成自己的5个视图扩展类,返回格式符合公司规范
        
    -9个视图子类
    	-5个:5个视图扩展类+GenericAPIView
        -List和Create的组合
        -其它组合
        
    -视图集
    	-ViewSetMixin:只要继承他,路由写法变了
        -ViewSet:ViewSetMixin, views.APIView
        -GenericViewSet:(ViewSetMixin, generics.GenericAPIView)
        -ModelViewSet:
        	-1 继承了ModelViewSet,没增加一条记录,就干某个事,重写perform_create
            -2 序列化使用一个序列化类,反序列化使用配置的那个序列化类
            -3 自定义了一个方法:login,使用了action装饰器,让它取到的qs对象和序列化类跟配置的都不一样
        -ReadOnlyModelViewSet
    
    -视图类 :self  内部有request对象,还有个action,就是视图类中方法的字符串名

 # 路由
	
 # 5 认证,频率,权限
	-认证:写个类,继承BaseAuthentication,重写authenticate方法,在里面做认证,如果认证通过返回两个值,如果认证失败,就抛异常-----》局部,全局配置
    -权限:同上
    -频率:写个类,继承SimpleRateThrottle,重写get_cache_key,返回什么,就以什么做限制[ip,用户id]---》类属性:scope---》配置文件配置----》全局和局部配
    
    -频率类:写个类,继承BaseThrottle,重写allow_request,如果频率运行,就返回True,如果频率不允许就返回False
    
# 6 自己写频率,同样的ip,一分钟只能访问3次---》频率类
    # 自定义的逻辑{ip1:[第二次访问时间,第一次访问时间],ip2:[第一次访问时间,],ip3:[当前时间]}
    #(1)取出访问者ip
    #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
    
# 7 排序  
	-使用内置的即可,查询所有接口,才有排序,必须继承GenericAPIView
    -在视图类中,配置filter_backends=[OrderingFilter],还需要配置属性:ordering_fields=[id,price]
    
    
    -如果是继承APIView写排序,自己写
# 8 过滤
	-内置:SearchFilter, 模糊查询
    -第三方:djagno-filter
    -自己写的:写一个类,继承BaseFilterBackend,重写filter_queryset,返回qs对象,返回的就是过来后的

# 9 分页
	-三种分页方式:PageNumberPagination,LimitOffsetPagination,CursorPagination
    -每个有些类属性控制:每页显示多少条,最多显示多少条,第几页。。。
    -写个类,继承三个之一,配置在视图类上(必须继承GenericAPIView)
    
    -继承APIView写分页


# 10 异常处理()
	-全局异常处理----》统一返回格式
    -写个函数:def custom_exception_handler(exc, context)---》调用了原来的exception_handler(只能处理drf的异常)---》返回两种情况:None,Response的对象
    -配置在drf的配置文件
    
# 11 接口文档编写
	-corapi自动生成
    
# 12 JWT认证  
	-base64
	-是什么
    -三段式
    -开发重点:签发,认证
    	-登录接口
        -认证类
        
    -djangorestframework-jwt
    	-基于auth的user表快速签发
        -基于内置的认证类认证,配合权限
        
        -登录成功返回格式
        
        -自定义用户表---》签发---》登录接口
        -自定义用户表---》认证---》认证类
        
        
        -扩写了auth的user表
        	-快速签发---》只能使用用户名密码,不能多方式登录
            -自定义登录接口---》实现多方式登录

 # rbac



 # 补充:
	魔法方法:类中使用__开头  __结尾的方法,它不需要主动调用,某种情况下会自动触发
    object类身上的
    
    
    序列化类源码分析之---》many参数的作用
    	-如果传了many=True,序列化多条----》得到的对象不是BookSerializer对象,而是ListSerialzier的对象中套了一个个的BookSerializer对象
        -如果传了many=False,序列化单条---》得到的对象就是BookSerializer的对象
        
        -类实例化得到对象,是谁控制的?
        def __new__(cls, *args, **kwargs):
            if kwargs.pop('many', False):
                return cls.many_init(*args, **kwargs)
            return super().__new__(cls, *args, **kwargs)
        
        
# 序列化
# 视图类
# 三大认证
# 排序,过滤,分页
# 全局异常处理
# jwt

继承BaseThrottle的频率类

import time
from rest_framework.throttling import BaseThrottle
time_num = 60
dic = {}
class NewThrottle(BaseThrottle):
    def allow_request(self, request, view):
        ip = request.META.get('REMOTE_ADDR')
        if ip == None:
            return True
        if ip in dic:
            dic[ip].insert(0,time.time())
        else:
            dic[ip] = [time.time()]
        if len(dic[ip]) > 5:
            time_d = dic[ip][5] - dic[ip][1]
            dic[ip].pop()
            if time_d > 60:
                return True
            else:
                return False
        else:
            return True

继承SimpleRateThrottle的频率类

from rest_framework.throttling import SimpleRateThrottle

class MyThrottle(SimpleRateThrottle):
    scope = 'lisi'
    def get_cache_key(self, request, view):
        ip = request.META.get('REMOTE_ADDR')
        return ip

中间件写频率类

import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
time_dic = {}
class middleThrottle(MiddlewareMixin):
    def process_request(self, request):
        ip = request.META.get('REMOTE_ADDR')
        if ip in time_dic:
            time_dic[ip].append(time.time())
        else:
            time_dic[ip] = [time.time()]
            if len(time_dic[ip]) > 5:
                time_d = time_dic[ip][5] - time_dic[ip][1]
                time_dic[ip].pop(0)
                print(time_d)
                if time_d > 60:
                    return None
                else:
                    return HttpResponse("访问频繁还要等待%s秒" % (60 - int(time_d)))

基于API的过滤排序分页

# 过滤
from django.db.models import Q
from rest_framework.filters import BaseFilterBackend


class publishfilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        name = request.query_params.get('name')
        addr = request.query_params.get('addr')
        if name and addr:
            queryset = queryset.filter(Q(name__contains=name) | Q(addr__contains=addr))
            return queryset
        elif name and (not addr):
            queryset = queryset.filter(name__contains=name)
            return queryset
        elif addr and (not name):
            queryset = queryset.filter(addr__contains=addr)
            return queryset
        else:
            return queryset
# 排序
from rest_framework.filters import OrderingFilter
# 分页
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class MyPageNumberPagination(PageNumberPagination):
    page_size = 2  # 每页多少条
    page_query_param = 'page'  # 在地址中page=第几页
    page_size_query_param = 'page_size'  # 在地址中page_size是每页显示多少条
    max_page_size = 6  # 每页最大多少条

    
    
    
# 视图 
class PublishView(APIView):
    ordering_fields = ['addr', 'id']

    def get(self, request):
        queryset = publish.objects.all()
        filtration = publishfilter()  # 实例化过滤类
        queryset = filtration.filter_queryset(request, queryset, self)  # 调用内部的过滤方法
        ordering = OrderingFilter()  # 实例化排序类,注意要给一个列表
        queryset = ordering.filter_queryset(request, queryset, self)  # 调用排序类里的排序方法
        paginator = MyPageNumberPagination()  # 实例化分页类
        qs = paginator.paginate_queryset(queryset, request, self)  # 调用分页方法
        ser = PublishSerialzier(instance=qs, many=True)
        # return paginator.get_paginated_response(ser.data)  # 调用分页类的内置方法
        return Response(OrderedDict([
            ('count', paginator.page.paginator.count),
            ('next', paginator.get_next_link()),
            ('previous', paginator.get_previous_link()),
            ('results', ser.data)
        ]))

全局异常处理

from rest_framework.response import Response
from rest_framework.views import exception_handler


def my_exception_handler(exc, context):
    e = exception_handler(exc, context)
    if e:
        if isinstance(e.data, dict):
            detail = e.data.get('detail')
        else:
            detail = e.data
        return Response({"code": 999, "msg": detail})
    else:
        return Response({"code": 1000, "msg": str(exc)})

自定义登陆接口

# jwt认证
import jwt
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.serializers import jwt_decode_handler

from .models import *

"""
authentication_classes = [JSONWebTokenAuthentication] # 认证类,drf-jwt提供的
permission_classes = [IsAuthenticated]
"""


class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        if request.method == 'GET':
            token = request.META.get('HTTP_TOKEN')
            try:
                payload = jwt_decode_handler(token)
                user_id = payload.get('user_id')
                user = User.objects.get(pk=user_id)

            except jwt.ExpiredSignature:
                raise AuthenticationFailed('token过期')
            except jwt.DecodeError:
                raise AuthenticationFailed('解码失败')
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('token认证异常')
            except Exception:
                raise AuthenticationFailed('token认证异常')
            return user, token

# 视图
# from rest_framework_jwt.utils import jwt_payload_handler
class Userview(ViewSet):
    authentication_classes = [JWTAuthentication]

    def check(self, request):
        return Response({'username':request.user.username})

    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username).first()
        pwd = user.password
        res = pbkdf2_sha256.verify(password, pwd)
        if res:
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response(common_response(token, user, request))
        else:
            return Response(common_response(code=100, msg='用户名或者密码错误'))

自定义注册接口

class UserDetailView(ViewSet):
    def reg(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        email = request.data.get('email')
        age = request.data.get('age')
        phone = request.data.get('phone')
        if not all((username, password, phone, email, age)):
            raise APIException('用户名,密码,手机号,邮箱,年龄,均不能为空')
        salt = uuid.uuid4()
        new_pwd = pbkdf2_sha256.hash(password, rounds=200000, salt=salt.bytes)
        User.objects.create(username=username, password=new_pwd, email=email, age=age, phone=phone)
        return Response(common_response(msg='注册成功'))