断点调试使用

# 程序在debug模式运行,可以在任意位置停下,查看当前情况下变量数据的变化情况

# 使用pycharm调试程序
- 1.以debug模式运行、
- 2.在代码左侧加入断点(红圈)
- 3.step over 单步调试
- 4.step into 进入该函数内部运行
- 5.绿色箭头 快速调到下一个断点

认证,权限,频率源码分析

权限类的执行源码

# 权限的源码执行流程
- 写一个权限类,局部使用,配置在视图类的,就会执行权限类的has_permission方法,完成权限校验 #drf的apiview,在执行视图类的方法之前,执行了三大认证 ---> dispatch方法中的
- self.initial(request, *args, **kwargs) ---> 执行3大认证 # # APIView类的399行左右:
def initial(self, request, *args, **kwargs):
# 能够解析的编码,版本控制。。。。
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # 认证组件的执行位置
self.perform_authentication(request)
# 权限组件 [读它]
self.check_permissions(request)
# 频率组件
self.check_throttles(request) # APIView的326 左右
def check_permissions(self, request):
# self.get_permissions()----》[CommonPermission(),]
# permission 是我们配置在视图类上权限类的对象
for permission in self.get_permissions():
# 权限类的对象,执行has_permission,这就是为什么我们写的权限类要重写has_permission方法
# self 是视图类的对象,就是咱们自己的的权限类的has_permission的view参数
if not permission.has_permission(request, self):
# 如果return 的是False,就会走这里,走这里是,没有权限
# 如果配了多个权限类,第一个没过,直接不会再执行下一个权限类了
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
) # APIView的274行左右 get_permissions
def get_permissions(self):
# self.permission_classes 是咱们配置在视图类上的列表,里面是一个个的权限类,没加括号
# permission_classes = [CommonPermission]
# [CommonPermission(),] 本质返回了权限类的对象,放到列表中
return [permission() for permission in self.permission_classes] ## 总结:
-APIView---dispatch----》initial---》倒数第二行---》self.check_permissions(request)
里面取出配置在视图类上的权限类,实例化得到对象,一个个执行对象的has_permission方法,如果返回False,就直接结束,不再继续往下执行,权限就认证通过 -如果视图类上不配做权限类:permission_classes = [CommonPermission],会使用配置文件的api_settings.DEFAULT_PERMISSION_CLASSES
优先使用项目配置文件,其次使用drf内置配置文件

认证源码分析

# 之前读过:drf的apiview,在执行视图类的方法之前,执行了3大认证----》dispatch方法中的
-497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 # APIView类的399行左右:
def initial(self, request, *args, **kwargs):
# 能够解析的编码,版本控制。。。。
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # 认证组件的执行位置【读它】
self.perform_authentication(request)
# 权限组件
self.check_permissions(request)
# 频率组件
self.check_throttles(request) # APIView的316行左右
def perform_authentication(self, request):
request.user #咱们觉得它是个属性,其实它是个方法,包装成了数据属性 # Request类的user方法 219行左右
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user # self 是Request的对象,找Request类的self._authenticate() 373 行
def _authenticate(self):
# self.authenticators 我们配置在视图类上认证类的一个个对象,放到列表中
# Request类初始化的时候,传入的
for authenticator in self.authenticators:
try:
# 返回了两个值,第一个是当前登录用户,第二个的token,只走这一个认证类,后面的不再走了
# 可以返回None,会继续执行下一个认证类
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise if user_auth_tuple is not None:
self._authenticator = authenticator
# 解压赋值:
#self.user=当前登录用户,self是当次请求的新的Request的对象
#self.auth=token
self.user, self.auth = user_auth_tuple
return self._not_authenticated() # self.authenticators 去Request类的init中找 152行左右
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
.....
self.authenticators = authenticators or ()
..... # 什么时候调用Reqeust的__init__?---》APIVIew的dispatch上面的492行的:request = self.initialize_request(request, *args, **kwargs)-----》385行----》
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
) # 总结:
1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行
2 自己写的认证类,可以返回两个值或None
3 后续可以从request.user 取出当前登录用户(前提是你要在认证类中返回)

频率源码分析

# 之前读过:drf的apiview,在执行视图类的方法之前,执行了3大认证----》dispatch方法中的
-497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 # APIView类的399行左右:
def initial(self, request, *args, **kwargs):
# 能够解析的编码,版本控制。。。。
self.format_kwarg = self.get_format_suffix(**kwargs)
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme # 认证组件的执行位置
self.perform_authentication(request)
# 权限组件
self.check_permissions(request)
# 频率组件【读它】
self.check_throttles(request) # APIView 的352行
def check_throttles(self, request):
throttle_durations = []
#self.get_throttles() 配置在视图类上的频率类的对象,放到列表中
# 每次取出一个频率类的对象,执行allow_request方法,如果是False,频率超了,不能再走了
# 如果是True,没有超频率,可以直接往后
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait()) if throttle_durations:
# Filter out `None` values which may happen in case of config / rate
# changes, see #1438
durations = [
duration for duration in throttle_durations
if duration is not None
] duration = max(durations, default=None)
self.throttled(request, duration) # 总结:
-我们写的频率类:继承BaseThrottle,重写allow_request,在内部判断,
如果超频了,就返回False,如果没超频率,就返回True

自定义频率类

## 视图类views.py
from rest_framework.viewsets import ViewSetMixin
from rest_framework.generics import ListAPIView
from .models import Book
from .serializer import BookSerializer
from .permissions import CommonPermission
from .throttling import SuperThrottle
class BookView(ViewSetMixin,ListAPIView):
queryset = Book.objects,all()
serializer_class = BookSerializer
authentication_classes = []
permission_classes = []
throttle_classes = [SuperThrottle]


## throttling.py
class SuperThrottle(BaseThrottle):
VISIT_RECORD = {} def __init__(self):
self.history = None def allow_request(self, request, view):
# 自己写逻辑,判断是否超频
# (1)取出访问者ip
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, ]
return True
# self.history = [时间1]
self.history = self.VISIT_RECORD.get(ip,[])
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
while self.history and ctime - self.history[-1] > 60:
self.history.pop()
# (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])

SimpleRateThrottle

# 写一个频率类,重写allow_request方法,在里面实现频率控制

# 1.SimpleRateThrottle---》allow_request源码
def allow_request(self, request, view):
# 这里就是通过配置文件和scop取出 频率限制是多少,比如一分钟访问5此
# self.rate是属性,所有应该去__init__内找 >>>:(2)
if self.rate is None:
return True # 返回了ip,就以ip做限制
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
# 下面的逻辑,跟咱们写的一样
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()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success() # 2.SimpleRateThrottle的init方法
def __init__(self):
if not getattr(self, 'rate', None):
# self.rate= '5、h'
# 2.读self.get_rate() >>>:(3)
self.rate = self.get_rate()
# 5 36000
# 4.读self.parse_rate()
self.num_requests, self.duration = self.parse_rate(self.rate) # 3.SimpleRateThrottle的get_rate() 方法
def get_rate(self):
if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
try:
# self.scope 是 lqz 字符串
# return '5/h'
return self.THROTTLE_RATES[self.scope]
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg) # 4.SimpleRateThrottle的parse_rate 方法
def parse_rate(self, rate):
# rate是'5/h'
if rate is None:
return (None, None)
# num =5 period= 'hour'
num, period = rate.split('/')
# num_requests=5
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
# (5,36000)
return (num_requests, duration)

基于APIView编写分页

# 分页功能 只有查询所有才有
from rest_framework.views import APIView
from rest_framework.response import Response from .page import CommonPageNumberPagination,CommonLimitOffsetPagination,CommonCursorPagination
# 查询所有
class BookView(ViewSetMixin, APIView):
def list(self, request):
books = Book.objects.all()
# 使用步骤
# 1.实例化得到一个分页类的对象
paginator = CommonLimitOffsetPagination()
# 2.调用分页类对象的paginate_queryset方法来完成分页,返回的page是要序列化的数据,分页
page = paginator.paginate_queryset(books, request, self)
if page is not None:
serializer = BookSerializer(instance=page, many=True)
# 3 返回数据,调用paginator的get_paginated_response方法
# return paginator.get_paginated_response(serializer.data)
return Response({
'total': paginator.count,
'next': paginator.get_next_link(),
'results': serializer.data
})

异常处理

# 1. APIView ------》dispatch----》三大认证,视图类的方法,如果出了异常,就会被异常捕获,捕获后统一处理
# 2.drf内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理drf的异常
- 主动抛的非drf异常
- 程序出错了
"""
都不会被处理,
我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式----》能记录日志
"""
- 公司里一般返回 {code:100,'msg':'系统错误,请联系系统管理员'} # 3.写一个函数,内部处理异常 在配置文件中配置一下即可
def common_exception_handler(exc,context):
# exc 错误对象
# context: 上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象
# 只要走到这里 就要记录日志,只有出错了,才会执行这个函数
# 记录日志尽量详细
print('时间time.time,登录用户id:request.user,用户ip:request.META.get(''),请求方式methods,请求地址,执行的视图类,错误原因')
res = exception_handler(exc, context)
if res: # 有值,说明返回了Response 对象,没有值说明返回None
# 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常
res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')})
else:
# res = Response(data={'code': 999, 'msg': str(exc)})
# 记录日志
res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'})
return res

drf从入门到飞升仙界 08的更多相关文章

  1. Docker从入门到飞升:基础配置安装

    导读 Docker近几年非常火,因为它是容器虚拟化,更能够充分提高硬件资源的使用率.其实利用率高不算什么,它最大的优势是能给让运维人员或者开发人员快速部署和交付资源,大大提高了工作效率.几乎所有的大企 ...

  2. Linux性能优化从入门到实战:08 内存篇:内存基础

    内存主要用来存储系统和应用程序的指令.数据.缓存等. 内存映射   物理内存也称为主存,动态随机访问内存(DRAM).只有内核才可以直接访问物理内存.   Linux 内核给每个进程都提供了一个独立的 ...

  3. npm 与 package.json 快速入门教程

    npm 与 package.json 快速入门教程 2017年08月02日 19:16:20 阅读数:33887 npm 是前端开发广泛使用的包管理工具,之前使用 Weex 时看了阮一峰前辈的文章了解 ...

  4. Python+Selenium基础入门及实践

    Python+Selenium基础入门及实践 32018.08.29 11:21:52字数 3220阅读 23422 一.Selenium+Python环境搭建及配置 1.1 selenium 介绍 ...

  5. drf源码剖析系列(系列目录)

    drf源码剖析系列(系列目录) 01 drf源码剖析之restful规范 02 drf源码剖析之快速了解drf 03 drf源码剖析之视图 04 drf源码剖析之版本 05 drf源码剖析之认证 06 ...

  6. Python学习从入门到放弃?我不允许!!!

    嗨,大家好 这里是汐仔 很多人都说学习python学习python,打开书本,三分钟,从入门到放弃. 这怎么可以!!!大家能选择python的原因可能是看它既简单,好入门,现在俨然是语言中的一匹黑马. ...

  7. 刻录DVD_目录

    1.down.52pojie.cn (20160701) (1 in 5) 工具 2.down.52pojie.cn (20160701) (2 in 5) Android VM 6/7 3.down ...

  8. .NET Core 3.0即将发布!

    期待已久的.NET Core 3.0即将发布! .NET Core 3.0在.NET Conf上发布.大约还有9个多小时后,.NET Conf开始启动. 为期3天的大概日程安排如下: 第1天-9月23 ...

  9. JavaEE在职加薪课好客租房项目实战视频教程

    JavaEE在职加薪课好客租房项目实战视频教程课程介绍:       本课程采用SOA架构思想进行设计,基于目前主流后端技术框架SpringBoot.SpringMVC.Mybaits.Dubbo等来 ...

  10. Easyui入门视频教程 第08集---登录实现 ajax button的使用

    目录 ----------------------- Easyui入门视频教程 第09集---登录完善 图标自定义   Easyui入门视频教程 第08集---登录实现 ajax button的使用  ...

随机推荐

  1. JavaWeb项目编译前后的目录结构

    JavaWeb项目编译前后的目录结构 编译前 页面和视图都放在webapp目录下 编译后 webapps WEB-INF

  2. [python] 基于paramiko库操作远程服务器

    SSH(Secure Shell)是一种网络安全协议,能够使两台计算机安全地通信和共享数据.目前,SSH协议已在世界各地广泛使用,大多数设备都支持SSH功能.SSH的进一步说明见:深入了解SSH.SS ...

  3. [深度学习] tf.keras入门2-分类

    目录 Fashion MNIST数据库 分类模型的建立 模型预测 总体代码 主要介绍基于tf.keras的Fashion MNIST数据库分类, 官方文档地址为:https://tensorflow. ...

  4. cmd/批处理常用命令

    启动新窗口执行命令 ::执行完毕以后,新开的窗口不会自动关闭 start cmd /k echo 123 ::执行完毕以后,新开的窗口会自动关闭 start cmd /C "echo 123 ...

  5. Educational Codeforces Round 141 解题报告

    Educational Codeforces Round 141 解题报告 \(\text{By DaiRuiChen007}\) \(\text{Contest Link}\) A. Make it ...

  6. 最新版 Proteus 8.15 Professional 图文安装教程 [ 附安装包 ]

    前言 Proteus 是世界上唯一将电路仿真软件.PCB设计软件和虚拟模型仿真软件三合一的设计平台. Proteus 8.15 现已发布,本篇将带领大家安装此版本. 介绍 Proteus Proteu ...

  7. SSM使用PageHelper

    第一步---->导入Maven依赖 <!--pageHelper--> <dependency> <groupId>com.github.pagehelper ...

  8. vue3和百度地图关键字检索 定位 点击定位

    效果图 在index.html中引入 百度地图开放平台 去申请你的ak 非常的简单可以自己百度 一下 <!-- 这个用官网给的有好多警告 更具百度的把 https://api.map.baidu ...

  9. vue学习笔记(七)---- vue中的路由

    一.什么是路由 对于普通的网站来说,所有的超链接都是URL地址,所有的URL地址都对应服务器上对应的资源 对应单页面的应用程序来说,主要通过URL中的hash(#号)来实现不同页面之间的切换,同时,h ...

  10. drf-drf请求、响应、基于GenericAPIView+5个视图扩展类

    1.反序列化类校验部分源码分析(了解) 1.当我们在视图类中生成一个序列化类对象ser,并且用ser.is_valid()是就会执行校验,校验通过返回True,不通过返回False.首先对象ser和序 ...