DRF大回顾
1.drf之第一天入门知识2.RESTful API规范,序列化,Django Rest_Framework3.cbv源码分析,APIView执行流程分析,序列化组件,序列化组件快速使用之序列化4.反序列化之更新,高级用法source(了解),高级用法定制字段,多表关联序列化和反序列化5.同时创建作者和作者详情表,ModelSerializer使用,模块与包的使用,反序列化校验源码分析,断言,drf之请求,魔法方法之 . 拦截6.drf之请求,drf 之响应,drf之响应格式,两个视图基类,基于GenericAPIView,5个视图扩展类7.9个视图子类,视图类,视图集,ViewSetMixin, drf之路由8. 认证组件,权限组件,频率组件,排序9.过滤,分页,异常处理10.接口文档,jwt介绍和构成,jwt签发与认证,base64编码,drf-jwt使用,django-rest-framework-jwt快速使用,定制签发返回格式,源码分析11.jwt自定义表签发, jwt 多方式登录(auth的user表)12.基于自定义表编写认证类,jwt源码分析,权限介绍,simpleui的使用
13.DRF大回顾
汇总
# 1 drf 入门规范
# 2 序列化组件---(最重要)
# 3 请求与响应
# 4 视图组件----(重要)
# 5 路由组件
# 6 认证,权限,频率---重要
# 7 过滤,排序,分页,全局异常
# 8 接口文档
# 9 jwt认证
# 10 权限:acl,rbac
1 drf入门规范
# 1 前后端开发模式
-混合(前后端不分离)---》图书管理系统,bbs项目
-分离:后端只负责写接口
-5个接口
# 2 API 接口
-后期写的那些接口,前后端交互的媒介
-请求地址
-请求方式
-请求参数:地址,请求体
-返回数据
-写了接口给谁用? 前端(web,app),提供给第三方调用
# 3 接口测试工具--postman
-发送http请求工具
-get请求可以在请求体中携带数据
-get请求和post请求有什么区别?
-请求编码格式:
urlencoded:key=value&key=value---->后端django--》request.POST
form-data: 数据和文件混到一起-----》后端django--》request.FILES,request.POST
json: {name:lqz,age:19}-----》后端django--》request.body---->装饰器
-collections创建,保存导出
# 4 接口规范:restful规范
-10条
-http和https区别?
# 5 序列化和反序列化
-序列化:把我们识别的数据转成对方识别的数据:字典,列表---》转成json格式字符串的过程
-反序列化:别人提供给我们数据转成我们识别的格式---》json格式字符串---》字典,对象。。
# 6 drf:django框架上的一个app,方便我们快速编写符合restful规范的接口
-快速写了5个接口
# 7 cbv源码分析
-路由 视图类.as_view()(request) 执行视图类中跟请求方式同名的方法
# 8 APIView的执行流程
-执行视图类中跟请求方式同名的方法
-去除了csrf认证
-包装了新的request
-执行了三大认证
-处理了全局异常
-视图类的对象 self.request
2 序列化组件
# 1 序列化组件作用
1 可以序列化--》qs对象,单个对象
2 反序列化--》前端传入的json格式数据---》保存到数据库
3 反序列化的校验:字段自己,局部钩子,全局钩子
# 2 快速使用
-写个类,继承Serializer
-在类中写字段
-在视图类中:实例化得到序列化类的对象:多条,和单条
-序列化:序列化类的对象.data retrun Response(ser.data)
# 3 常用字段类
-跟models中之前学过的有对应关系
-ListField DictField --->序列化和反序列化都会用
# 4 字段参数
-限制反序列化校验字段自己规则的
-read_only write_only:控制字段只做序列化或反序列化的,如果不写就是
# 5 使用序列化类做反序列化
-新增
ser=BookSerializer(data=request.data)
ser.is_valid(raise_exception=True)--->只要校验不通过,直接抛异常
ser.save() # 继承Serializer,需要在序列化类中重写create方法,完成新增
序列化类中重写create方法
def create(self, validated_data):
# validated_data 前端传入,校验过后的数据
# 新增一个对象
return 新增的对象 # 后续再视图类中只要调用ser.data,这里必须返回
-修改
ser=BookSerializer(instance=要修改的对象,data=request.data)
ser.is_valid(raise_exception=True)--->只要校验不通过,直接抛异常
ser.save() # 继承Serializer,需要在序列化类中重写update方法,完成修改
序列化类中重写update
def update(self, instance, validated_data):
# instance 要修改的对象,哪里来的?BookSerializer(instance=要修改的对象的id,data=request.data)
# validated_data:校验过后数据
res=Book.objects.filter(pk=instance).update(**validated_data)
return res # 返回的res最后干啥了?只要在 视图类中 调用ser.data,他会根据这个返回值做序列化
# 6 反序列化校验
-字段自己
-局部钩子--》序列化类中写 validate_字段名 传参数
-全局钩子---》序列化类中写 validate 传参数,字典
-登录接口
# 7 定制返回格式
-source:1 改名 2 跨表查 3 拿表模型中的方法
-SerializerMethodField:在序列化类中写
-publish=serializers.SerializerMethodField(read_only)
-配合一个 get_username(self,obj)的方法,返回什么,这个字段就是什么
-在表模型中写:方法
publish=serializers.DictField()
-只能做序列化用了,反序列化得单独用---》需要使用read_only和write_only控制
# 8 ModelSerializer使用
class Meta:
model=表名
fields=[需要序列化或反序列化的字段,表中没有也要写]
extra_kwargs={} # 传入字段参数
重写字段
大部分情况下不需要写create和update了
局部,全局钩子跟之前一模一样
3 视图组件
# 1 两个视图基类
APIVIew
-类属性
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
GenericAPIView:要使用序列化类,数据库打交道,就可以继承它
-queryset = None
-serializer_class = None
-lookup_field = 'pk'
-filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
-pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
-get_queryset(self) #获取所有要序列化的数据,在里面拿到qs对象后要 .all()
-get_object # 获取单个对象
-get_serializer()--->内部调用了get_serializer_class--》最终返回序列化类的对象,传入要传的参数---》instance ,data many
-filter_queryset---》给ListModelMixin用了--》把配置的filter_backends 依次执行完成过滤
# 5个视图扩展类 =不是视图类,需要搭配GenericAPIView
ListModelMixin,
CreateModelMixin,
UpdateModelMixin,
DestroyModelMixin,
RetrieveModelMixin
一个视图类中写四个接口--》必须借助于ViewSetMixin
class BookView(ViewSetMixin,GenericAPIView,ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin):
queryset = None
serializer_class = None
# 6 9个视图子类
# 7 视图集
-ModelViewSet:重写方法实现你想要的功能
-ReadOnlyModelViewSet
-ViewSetMixin:路由写法变了
-ViewSet:ViewSetMixin+APIView
-GenericViewSet:ViewSetMixin+GenericAPIView
4 请求与响应
# 请求:
-请求源码:request对象
-request.data
-request.query_params
-跟之前一样
-request._request
-__getattr__
-请求能解析编码格式:parser_classes
-局部使用
-全局使用
# 响应
-响应Response 源码
-data:响应体的内容,序列化后的数据给了他
-headers:响应头 django原生响应头 响应对象['xx']=xxx
-status:响应状态码
-响应编码格式--》浏览器,postman,看到的样子
-局部和全局配置
# 写了个视图类 获取所有接口
class BookView(ViewSetMixin,GenericAPIView,ListModelMixin):
queryset = None
serializer_class = None
def list(self,request,*args,**kwargs):
res=super().list(request,*args,**kwargs)
res.data # {code:100,msg:成功,data:[{},{}]}
return Response({code:100,msg:成功,data:res.data})
5 路由组件
# 1 只要继承ViewSetMixin及其子类,路由写法就变了
-方式一:映射方式
视图类.as_view({'get':'lqz'})
-方式二:自动生成
-SimpleRouter DefaultRouter
# 2 action装饰器:methods detail
6 认证权限频率
# 认证的使用
1 写个类,继承BaseAuthentication
2 重写autenticate
3 取出用户传入的 token ---》通过token能知道是谁
4 认证通过取出用户,认证失败,抛出异常
5 return 查到的对象,token---》后续再request.user中就是当时返回的第一个值
6 视图类中配置
7 全局配置
# 权限类
1 写个类,继承BasePermission
2 has_permission
3 判断用户的权限
acl 权限,取出当前用户所有权限,判断当次请求是否在权限中
rbac,根据当前用户取出所有角色,通过角色取出所有权限,去个重,判断是否在权限中
4 有权限返回True,没有返回False
5 视图类中配置
6 全局配置
# 频率类
1 写个类,继承SimpleRatethrottle
2 重写get_cache_key 返回值 唯一的 ip,userid
3 类属性 scope='字符串'
4 配置文件配置
'DEFAULT_THROTTLE_RATES': {
'字符串': '3/m',
},
5 视图类中配置
6 全局配置
权限---》认证---》频率
为什么我们写了 权限类,配置行,它就会执行权限控制?
-APIView 的dispatch中执行了3大认证---》
def dispatch(self, request, *args, **kwargs):
self.initial(request, *args, **kwargs) # 三大认证
- self.initial 需要从根上
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
self.check_permissions(request) # 权限类的执行位置
self.check_throttles(request)
-self.check_permissions(request)
def check_permissions(self, request):
# 你配置在视图类上一个个权限类的对象列表 [权限对象1,权限对象2]
for permission in self.get_permissions():
# self,是视图类的对象,因为在APIView中
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
- def get_permissions(self):
return [permission() for permission in self.permission_classes]
返回一个列表,列表中的每个元素都是从类的permission_classes属性中定义的权限类实例化的权限对象。
认证类源码---》难
-APIView 的dispatch中执行了3大认证---》
def dispatch(self, request, *args, **kwargs):
self.initial(request, *args, **kwargs) # 三大认证
- self.initial 需要从根上
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)# 认证类的执行位置
self.check_permissions(request)
self.check_throttles(request)
# 它接收一个请求对象作为参数,并从该请求对象中获取用户对象。
def perform_authentication(self, request):
request.user
-去Request类中找user---》方法包装成了数据属性
@property
def user(self):
如果有就return
并将验证后的用户对象赋值给_user属性。
if not hasattr(self, '_user'):
这个不用看
with wrap_attributeerrors():
如果不存在,它会调用_authenticate()方法进行身份验证,
self._authenticate()
return self._user
最后,它返回_user属性的值。
去Request类中找self._authenticate()
def _authenticate(self):
for authenticator in self.authenticators: # 配置在视图类上一个个认证类的对象列表
try:
# 调用认证类对象的authenticate方法,传入 self,是request对象
user_auth_tuple = authenticator.authenticate(self) # 当前登录用户,token
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
#self就是request对象,后续 request.user就是认证类返回的第一个参数
self.user, self.auth = user_auth_tuple # 解压赋值
return
self._not_authenticated()
-认证类可以配置多个,但是如果有一个返回了,后续的就不走了
-self.authenticators 是request对象的属性,是在Request实例化的时候传入的,它什么时候实例化的,包装新的Reqeust时传入的---》APIView的dispatch--》
7.1 频率源码分析
1 频率源码
-APIView----disaptch---》self.initial(request, *args, **kwargs)---》416行:self.check_throttles(request)----》352行 check_throttles
def check_throttles(self, request):
# self.get_throttles()就是咱们配置在视图类上频率类的对象列表[频率类对象,]
for throttle in self.get_throttles():
# 执行频率类对象的allow_request,传了2个,返回True或False
if not throttle.allow_request(request, self):
# 反会给前端失败,显示还剩多长时间能再访问
throttle_durations.append(throttle.wait())
2 频率类要写
1 写一个类,继承,BaseThrottle
2 在类中重写:allow_request方法,传入 3个参数
3 在allow_request写限制逻辑,如果还能访问--》返回True
4 如果超了次数,就不能访问,返回False
5 局部配置在视图类上
6 全局配置在配置文件中
3 我们在drf中写的时候,不需要继承 BaseThrottle,继承了SimpleRateThrottle,重写get_cache_key
-我们猜测:一定是 SimpleRateThrottle帮咱们写了咱们需要写的
4 自定义频率类,实现一分钟只能访问三次的控制:
(1)取出访问者ip
(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
5 SimpleRateThrottle 源码分析
- SimpleRateThrottle内部一定有:allow_request---》
def allow_request(self, request, view):
# 咱们没写,以后咱们可以在频率类中直接写
# rate='3/m' 以后不用写scope了,就会按一分钟访问3次现在
if self.rate is None:
return True
# 取出:重写的get_cache_key返回的值,咱们返回了访问者ip
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
# 根据当前访问者ip,取出 这个人的访问时间列表 [访问时间1,访问2,访问3,访问4]
self.history = self.cache.get(self.key, [])
# 取出当前时间
self.now = self.timer()
# 把访问时间列表中超过 限制时间外的时间剔除
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
# 判断访问时间列表是否大于 3
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success()
自定义频率类
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
# VISIT_RECORD = {'192.168.1.1':[当前时间,当前时间,访问时间列表]}
VISIT_RECORD = {}
def __init__(self):
self.history = []
def allow_request(self, request, view):
ip = request.META.get('REMOTE_ADDR')
import time
ctime = time.time()
# (2)判断当前ip不在访问字典里,说明是第一次访问,添加进去,并且直接返回True,表示第一次访问
if ip not in self.VISIT_RECORD:
self.VISIT_RECORD[ip] = [ctime, ] # VISIT_RECORD = {'192.168.1.1':[当前时间2,当前时间1,]}
return True
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
self.history = self.VISIT_RECORD[ip] # 访问时间列表
while self.history and ctime - self.history[-1] > 60: # 循环删除1分钟之前访问的实际
self.history.pop()
# 最后self.history都剩下是一分钟之内的时间了
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
if len(self.history) < 3:
self.history.insert(0, ctime)
return True
else:
return False
def wait(self):
import time
ctime = time.time()
return 60 - (ctime - self.history[-1])
7.2 过滤排序分页全局异常
7.2.1 过滤
#1 针对于查询所有接口---》继承:GenericAPIView+ListModelMixin---》只需要在视图类中写一个类属性---》filter_backends = [过滤类,过滤类2]
# 过滤类:
1 内置的:SearchFilter 类属性:search_fields=[可以按字段过滤]
127.0.0.1:8080/books/?search=红
2 第三方
-django-filter
-精准匹配
127.0.0.1:8080/books/?name=红楼梦
-能更强大
3 自定义的
写一个类,继承:BaseFilterBackend
重写 filter_queryset
在filter_queryset中完成过滤,会把qs传入,返回过滤后的qs即可
# 2 源码分析
ListModelMixin---》list方法---》queryset = self.filter_queryset(self.get_queryset()) 过滤后的数据----》执行了GenericAPIView的---》filter_queryset---》取出一个个配置在视图类上的过滤类,依次实例化得到对象后执行对象的filter_queryset方法完成过滤---》最终返回的数据,就是过滤后的数据
# 3 继承APIView写过滤
request 取出过滤条件
book_list=Book.objects.all().filter(过滤)
7.2.2 排序
#1 针对于查询所有接口---》继承:GenericAPIView+ListModelMixin---》只需要在视图类中写一个类属性---》filter_backends = [排序类]
# 2 内置排序类即可:OrderingFilter---》配置类属性ordering_fields
# 自定义排序类,完成自己的规则
7.2.3 分页
#1 针对于查询所有接口-->才有分页
# 2 三种分页方式,必须继承分页类,重写几个类属性实现--->配置在视图类上--》继承:GenericAPIView+ListModelMixin
PageNumberPagination:用的最多,之前常见的分页方式,查询第几页,每页有多少条的分页方式 :page=10&size=3
LimitOffsetPagination:从第几条开始,取几条 offset=10&limit=3 从第10条开始,取3条
CursorPagination:只能上一页和下一页,需要排好序再分页
# 3 继承APIView 写分页
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=PageNumberPagination()
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
# return Response(ser.data)
# 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可)
return page.get_paginated_response(ser.data)
return Response(ser.data) # 只会有数据,不会有上一页和下一页,总条数
7.2.4 全局异常处理
# 前后端分离了,后期,后端出了异常,我们不想让前端看到,我们需要捕获全局异常,统一返回格式
# drf 源码中已经处理
APIView--->dispatch--->
try:
# 1 执行三大认证
# 2 执行视图类的方法
except Exception as exc:
response = self.handle_exception(exc)
-463行左右:
# exception_handler就是配置文件中配置的一个函数-->默认的
# 后期自己写了
exception_handler = self.get_exception_handler()
response = exception_handler(exc, context)
# 默认执行的是:rest_framework.views.exception_handler函数---》只处理了drf的异常
# 咱们处理全局异常的步骤:
1 写一个函数,在内部处理
from rest_framework.views import exception_handler
def common_exception_handler(exc,context):
res=exception_handler(exc,context)
if res: #这次异常是drf的,并且它处理了
# 我们要统一返回格式
return Response({'code':888,'msg':"系统异常(drf异常),请联系系统管理员:%s"%res.data.get('detail',None)})
else:
return Response({'code': 999, 'msg': "系统异常(非drf异常),请联系系统管理员:%s" % str(exc)})
2 配置在配置文件上
7.3 接口文档
# 接口写完,必须编写接口文档,给别人用
# 接口文档规范:要有什么东西
1 描述
2 地址
3 请求方式
4 请求编码格式
5 请求参数,详解
6 返回示例 json
7 返回示例中字段详解
# 编写的位置:
1 直接写在文件中共享(word,md)
2 平台上写
- 公司搭建的平台(自己研发,第三方开源)
- 使用第三方接口文档平台
3 自动生成
-coreaip
7.4 JWT认证
# JWT是什么
json web tokne缩写,一种前后端认证的方法,区别与session的方案,不需要在后端存储数据,也能实现会话保持
# jwt原理
-三段式:
-签发阶段
-登录:
用户名+密码
手机号+验证码
本机号码一键登录--》向后端就只传了手机号--》根据手机号签发token
-认证阶段
-drf中的认证类
-校验token是否合法
# 别人截获到token后,模拟发送请求
-1 设置过期时间---》10m
-2 需要登录后才能访问的接口,不仅要带token还是带个加密串 sign
-3 双token认证
-返回两个token:一个token:7天过期,一个token:10分钟过期
-以后用就只带10分钟过期的token
-过了10分钟了,10分钟token失效了
-携带着7的token到后端,生成一个10分钟的返回
-原来的token串使用md5+盐生成一个串--》签名
# django中使用jwt--》快速签发
# 快速签发定制返回格式
# 认证:jwt提供的认证 一个认证类,一个权限类
# 自定义用户表
签发
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
认证:认证类
token = request.META.get('HTTP_TOKEN')
payload = jwt_decode_handler(token)
user = User.objects.get(pk=payload.get('user_id'))
# 多方式登录(auth的user表)
-把校验和签发token逻辑写在序列化类的 全局钩子中了
-在全局钩子中,只要校验不通过,就抛异常
7.5 权限控制
# acl:访问控制列表
用户跟权限多对多
# 基于角色的访问控制 rbac
用户跟角色关系,角色和权限管理
-用户表
-角色表
-权限表
-用户和角色多对多中间表
-角色和权限多对多中间表
-用户和权限多对多
# abac:
基于属性+角色的访问控制
基于属性+访问控制列表
张三:【看视频,发视频】
张三:晚上12点凌晨7点 不能发视频
合集:
drf
分类:
python从入门到入狱
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构