def 序列化组件,,请求和响应__视图__路由组件__三大认证,,排序,过滤吗,全局异常,分页,,频率源码分析
前情回顾:
drf入门规范
-前后端开发模式:混合开发(不分离),前后端分离 -API接口 -接口测试:postman -测试:接口测试---》jmeter:java开发的软件 -restful规范:10 -drf 帮助我们快速实现符合restful规范的接口 -APIView执行流程--》重写了as_view--》重写了dispatch -1 去除了csrf认证 -2 包装了新的request -3 三大认证 ---》根据请求方式,执行视图类的方法 -4 处理了全局异常 -Request 类的对象 -1 多了data -2 重写了 __getattr__ 对象.属性 触发 -去旧的request中找:通过反射 getattr(self._request,'path') -3 以后用起来跟之前一样 -4 query_params
序列化组件
-作用: 1 序列化 -1 写一个类,继承(序列化类) -2 写字段,需要跟表中有对应关系(手动对应:Serializer 自动对应:ModelSerializer) -3 视图类中使用 -多条Queryset对象:使用orm查询出来的 -django执行原生sql---》[book1,book2]-->使用序列化类完成序列化 -ser=BookSerializer(instnce=qs,many=True) -ser.data+Response 2 反序列化 -1 写一个类,继承(序列化类) -2 写字段,需要跟表中有对应关系(手动对应:Serializer 自动对应:ModelSerializer) -3 视图类中使用 -修改ser=BookSerializer(instance=对象,data=request.data) -新增ser=BookSerializer(data=request.data) -ser.is_valid() -ser.save()--->触发序列化类的:update,create 3 序列化和反序列化用在一起 -read_only 和 write_only -extra_kwargs={} 3 校验 -三层 -Serializer -重写 update和create -ModelSerializer -重写字段 -class Meta: model fields extra_kwargs -定制返回格式:序列化 -source定制:表中字段,表中方法,跨表 -表模型中写方法 -序列化类中:SerializerMethod--》get_字段名 # BookReadSerializer # BookWriteSerializer # LoginSerializer class BookView(ModelViewSet): query_set=xx serializer_class=BookReadSerializer # 重写 get_serializer_class 方法 def get_serializer_class(self): # 影响:list retrieve update create 自己写的 if self.action=='list' or self.action=='retrieve': # if self.request.method=='get': # get_serializer() 获取使用哪个序列化类 return BookReadSerializer elif self.action=='login': return LoginSerializer else: return BookWriteSerializer def get_queryset(self): # list retrieve update create delete get_object # 会影响到谁? list,retrieve,update,delete # 不会影响到create 返回所有数据,但会影响 list,retrieve,update,delete ,自己写的 @action(methods=['POST'],detail=False) def login(self,requset): self.get_serializer() #让它拿到 LoginSerializer self.get_object() self.get_queryset
请求和响应
-请求: -Request源码 -能够解析的编码格式:默认三种:json,urlencoded,form-data -响应: -Response源码 -实例化的时候,可以传入的参数 data=None, 响应体中 status=None, 响应状态码 headers=None, 响应头 -list方法---》res=super().list(request)--->res 就是Response 类的对象 -res.status res.data res.headers -res.data [{},{},{}] -{code:100,msg:成功,results:res.data} -响应编码格式 # 4 视图组件 -APIView -1 继承了View -2 重写了 as_view -3 重写了 dipatch---》相当于给之前的视图类的方法加了装饰器 -4 写跟请求方式同名的方法:get,post,delete,put,get -GenericAPIView -1 继承了APIView -2 类属性 queryset =Book.object.all().filter(is_delete=False) # 要序列化的数据---》self.get_queryset拿到--》只要方法中使用了get_queryset,就会受它影响 serializer_class # 序列化类:list,create,put,retrieve,自己写的方法 #通过重写 get_serializer_class控制视图类的方法使用哪个序列化类 lookup_field = 'pk' # 分组,转换器出来的参数 filter_backends # 过滤和排序 pagination_class #分页 -3 方法 - get_serializer_class---》用来重写 - serializer_class---》获取序列化类---》list,retrieve(get_object间接),put,create - get_queryset--->要序列化的总数据或 单条查询的数据源 - get_object --》获取单条---》通过get_queryset拿到的 - filter_queryset--》过滤使用---》一般不重写--》必须配合list使用 -视图类中重写它--》可以完成过滤--》就不用配置过滤类了 -get_paginated_response --》获取分页后的返回数据--》必须配合list -通过重写它,定制分页后的返回数据格式 -def get_paginated_response(self, data): assert self.paginator is not None return Response({ 'code': 100, 'msg': 'asdfasfasfdasf', 'next': self.paginator.get_next_link(), 'pre': self.paginator.get_previous_link(), 'result': data}) -5个视图扩展类--》必须搭配GenericAPIView -ListModelMixin # 过滤,排序,分页 def list(self, request, *args, **kwargs): # 走了过滤和排序 queryset = self.filter_queryset(self.get_queryset()) # 处理了分页 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) # 如果没有分页,正常返回 serializer = self.get_serializer(queryset, many=True) return Response(data=serializer.data) -RetrieveModelMixin def retrieve(self, request, *args, **kwargs): instance = self.get_object() # 序列化单挑数据--》重写 serializer = self.get_serializer(instance) # 重写--》使用哪个序列化类 return Response(serializer.data) -CreateModelMixin def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) # 重写 serializer.is_valid(raise_exception=True) self.perform_create(serializer) #做了真正的保存serializer.save(),重写 return Response(serializer.data, status=status.HTTP_201_CREATED) def perform_create(self, serializer): #可以重写 serializer.save() -DestroyModelMixin def destroy(self, request, *args, **kwargs): instance = self.get_object() # 通过重写它,决定删除谁 self.perform_destroy(instance) # 重写--》软删除-》不在数据库删除-》is_delete # 设置为True,以后要序列化的时候,不查出来了 return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete() -UpdateModelMixin def update(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) self.perform_update(serializer) return Response(serializer.data) def perform_update(self, serializer): serializer.save() -9 个视图子类-->重写 get_serializer_class,get_queryset,get_object ListAPIView:GenericAPIView+ListModelMixin--》重写:list,get 写了个方法:get---》核心就是--》self.list RetrieveAPIView--》重写retrieve,get 写了个方法:get---》核心就是--》self.retrieve CreateAPIView-->重写create,perform_create 写了个方法:post---》核心就是--》self.create UpdateAPIView DestroyAPIView --------- ListCreateAPIView --------- RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
视图集
-ViewSetMixin # 路由写法变了---》多个视图类可以放到一起---》一个视图类中可以放多个方法 -GenericViewSet -ViewSet -ModelViewSet:有get,post这些方法吗?没有,只有list,create。。这些 -重写:get_serializer_class,get_queryset,perform_destroy -重写list,分页返回格式 -ReadOnlyModelViewSet:只有 list,retrieve -以后写视图类: 一般不会5个都写 -比如只想写 查询所有和删除一条--继承--》GenericViewSet,ListModelMixin,DestroyModelMixin
路由组件
- 映射写法:ViewSetMixin - 自动生成路由:ViewSetMixin---》用的多 - action装饰器:methods,detail - SimperRouter和DefaultRouter
三大认证
-认证类的使用 -权限类: -登录成功后,有没有权限---》request.user拿到登录用户,判断权限 -频率类: ip,用户id -用户访问记录:存在缓存中,默认在内存
排序,过滤,全局异常
-排序:内置排序 list接口,继承GenericAPIView+list -filter_backends 配置 排序类 -ordering=id,-price -过滤: -内置 -第三方 -自定义:写个类,继承:BaseFilterBackend,重写filter_queryset在内部完成过滤,返回过滤后的qs对象 -过滤,排序可以连用,多个过滤也可以连用 -全局异常: -写个函数,完成处理,配置到配置文件---》以后只要出了异常,都会走咱么的函数
分页
- list接口+GenericAPIView 配置分页类 - 三种分页方式 -都有自己的类属性 -继承APIView实现分页 from rest_framework.viewsets import ViewSet from rest_framework.mixins import ListModelMixin,RetrieveModelMixin from rest_framework.generics import GenericAPIView from . import pagination class BookView(ViewSet): def list(self, request): book_list = Book.objects.all() # 调用咱们写的分页类对象的paginate_queryset方法返回了 分页后的qs对象 # pagenation = pagination.CommonPageNumberPagination() # pagenation = pagination.CommonLimitOffsetPagination() pagenation = pagination.CommonCursorPagination() page = pagenation.paginate_queryset(book_list, request, self) ser = BookSerializer(instance=page, many=True) # return pagenation.get_paginated_response(ser.data) # CommonPageNumberPagination 返回格式 # return Response({'code': 100, # 'msg': '查询成功', # 'count': pagenation.page.paginator.count, # 'next': pagenation.get_next_link(), # 'pre': pagenation.get_previous_link(), # 'result': ser.data}) # CommonLimitOffsetPagination # return Response({ # 'code': 100, # 'msg': '查询成功', # 'count': pagenation.count, # 'next': pagenation.get_next_link(), # 'pre': pagenation.get_previous_link(), # 'result': ser.data}) # CommonCursorPagination return Response({ 'code': 100, 'msg': '查询成功', 'next': pagenation.get_next_link(), 'pre': pagenation.get_previous_link(), 'result': ser.data})
----------------------------------------------------------------------------------------------------------------------
复杂方案---》通用方案---》后期任何框架都可以使用这个逻辑 from rest_framework.throttling import BaseThrottle import time class MyThrottling(BaseThrottle): # 后来的时间,放到第一个位置 VISIT_RECORD = {} # 以后,访问者ip放在这个字典中 {192.168.1.11:[时间2,时间1],192.168.1.99:[时间3,时间2,时间1]} def __init__(self): self.history = None def allow_request(self, request, view): # 在这里,实现 频率限制 # (1)取出访问者ip ip = request.META.get('REMOTE_ADDR') # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走 ctime = time.time() if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [ctime, ] return True # (3)如果当前的ip,在访问列表中,循环判断当前ip的访问时间列表列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, self.history = self.VISIT_RECORD.get(ip) # 访问时间列表 # ctime - self.history[-1] 访问了最久的时间,如果大于60,说明 一分钟之前访问过的,这个时间是无效的,pop掉 while self.history and ctime - self.history[-1] > 60: 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])
# 1 APIView--->dispatch--->self.initial(request, *args, **kwargs)--->self.check_throttles(request) # 2 APIView的check_throttles def check_throttles(self, request): throttle_durations = [] # self.get_throttles() 是视图类中配置的一个个频率类的对象列表 for throttle in self.get_throttles(): # throttle.allow_request 返回false ,说明到频率了 if not throttle.allow_request(request, self): # 到了频率,走了它 # throttle.wait() 返回还剩多长时间能访问 throttle_durations.append(throttle.wait()) # 被频率限制住了,它就有值 if throttle_durations: durations = [ duration for duration in throttle_durations if duration is not None ] #duration=[35,54] # duration=[] duration = max(durations, default=None) # duration=56 # duration=None self.throttled(request, duration) # 3 self.throttled(request, duration) ---》APIView def throttled(self, request, wait): # wait=56或None raise exceptions.Throttled(wait) # 4 Throttled类实例化得到对象,传了数字进去 from rest_framework.exceptions import Throttled 内部 拼接错误信息--》但是是英文的,如果要换成中文 # 5 超过了频率,就不返回False了,直接抛异常 raise Throttled(None, '超过了限制,还剩:%s 时间' % self.wait())
1 咱们写的 CommonThrottling没有写allow_request class CommonThrottling(SimpleRateThrottle): rate = '3/m' def get_cache_key(self, request, view): return request.META.get('REMOTE_ADDR') # 2 一定是 SimpleRateThrottle 写了,完成了频率校验 # 3 SimpleRateThrottle--》allow_request---》跟咱们的逻辑一样 def allow_request(self, request, view): if self.rate is None: # 自己写了 '3/m' return True # 返回了 ip地址,self.key 就是访问者的ip地址 self.key = self.get_cache_key(request, view) if self.key is None: return True # self.history 当前ip,访问的时间列表,self.key是ip # self.cache 缓存,去缓中,根据ip,取出访问者时间列表,如果没有就是 [] self.history = self.cache.get(self.key, []) # 当前时间 self.now = self.timer() # timer = time.time # self.now 当前时间, # self.duration 就是60 while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() # self.num_requests 是3 if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success() # 4 self.duration 就是60 self.num_requests 是3 # 3 60 self.num_requests, self.duration = self.parse_rate(self.rate) #5 self.parse_rate(self.rate) def parse_rate(self, rate): if rate is None: # '3/minute' return (None, None) # 3 period=mmmmm num, period = rate.split('/') # 3 num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # duration 是60, # num_requests 是3 return (num_requests, duration)