django rest framework之部分组件
认证
在这里,笔者将登录与认证归一起,具体代码如下
视图函数
def md5(user):#用账户当参数 '''生成随机码''' import time import hashlib c_time = str(time.time()) m = hashlib.md5(bytes(user,encoding='utf-8')) m.update(bytes(c_time,encoding='utf-8')) return m.hexdigest() class AuthView(APIView): authentication_classes = [ ] def post(self,request,*args,**kwargs):#选择method请求方式 ret = {'code':1000,'msg':None} try: username = request._request.POST.get('username') password = request._request.POST.get('password') obj = UserInfo.objects.filter(username=username,password=password).first() if not obj: ret['code'] = 1001 ret['msg'] = '账号或者密码错误!' token = md5(username) print(token) models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})#新建或者修改数据 ret['msg'] = '欢迎登陆!' ret['token'] = token except Exception as e: pass return JsonResponse(ret) class OrderView(APIView): # authentication_classes = [Authtication,]#局部使用 def get(self,request,*args,**kwargs): print(request.user) print(request.auth) ret = {'code':1000,"msg":'ok','data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret)
为了规范,将认证放在单独的文件下:
代码如下:
class Authtication(BaseAuthentication):#继承父类header就可以省略,因为父类里面有 def authenticate(self,request): token = request._request.GET.get('token') token_obj = UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败!') #drf会将两个字段的值赋值给request,以供后续使用 return (token_obj.user,token_obj) # def authenticate_header(self,request): # pass
由于drf的机制,主要是列表生成式:在全局进行认证我们可以在配置文件中这么写:
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":["api.utils.auth.Authtication",] #这里必须是列表,否则会报错'type' object is not iterable }
接下来是源码的个人解读:
用户认证源码流程 1.请求进来之后,先在自己的类中寻找dispatch方法,由于我们没有自己定义,所以找到父类ApiView中的dispatch开始执行方法 2.进入dispatch方法之后,对request进行封装,request不再是原生的request(如果要使用可以用request._request调用) 封装的时候执行了一个authenticators=self.get_authenticators()方法,通过列表生成式实例化对象 3.封装完request之后,开始执行initial方法,其中我们的认证是self.perform_authentication(request)这个方法实现的 4.在三中的方法里面执行了request.user 5.然后执行self._authenticate()方法,主要用途是循环所有的authentication对象,执行所有authentication()方法 6.其实在这一步的时候我们已经可以用我们自己定义的类进行认证了 ,执行self._authenticate()方法时,
1.如果authenticate方法抛出异常,则执行self._not_authenticated()执行
2.如果有返回值,必须是元祖:(request.user,request.auth)
3.返回none,则交给下一个认证来处理 如果抛出错误,会继续抛出错误,也就是意味着会继续执行列表内的认证方法
7.通过认证之后,开始执行我们自己的get post等方法
梳理:
权限
部分代码
单独目录下的权限
class MyPermission(BasePermission): #继承内置权限 message = '您无权访问!' #修改通知 def has_permission(self,request,view): if request.user.user_type != 3: return False #提示没有权限 return True #有权限
配置文件与认证大体相同
权限源码流程
1.同认证一样,请求进来之后先执行dispatch方法
2.对request进行封装
3.继续执行initial方法
4.对于权限继续执行check_permission方法,循环权限对象
5.循环对象之后,执行我们自己定义的has_permission方法:通过为True,否则为False,且返回错误信息,可自己修改
节流
节流是为了限制用户访问次数而出现的组件
示例代码如下:
import time VISIT_RECORD = {} class VisitThrottle(object): '''节流''' def allow_request(self,request,view): remote_addr = request.META.get('REMOTE_ADDR') #获取用户IP c_time = time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [c_time,] return True history = VISIT_RECORD.get(remote_addr) self.history = history #将现在的history放在self.history中,以供其他地方调用 while history and history[-1] < c_time - 60:#大于60s的就从列表中删除 history.pop() if len(history) < 3: history.insert(0,c_time) return True def wait(self): c_time = time.time() return 60-(c_time - self.history[-1]) #显示还有多少时间可以继续访问
节流源码流程:
1.请求进来之后,依旧先执行dispatch()方法
2.对request进行封装
3.继续执行initial方法
4.get_throttle继续通过列表生成式实例化对象
5.执行我们自己写的allow_request(),True则继续,False则限制
节流的基于内置类实现的访问控制
版本控制
demo
配置文件
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":'rest_framework.versioning.URLPathVersioning', "DEFAULT_VERSION":'v1', 'ALLOWED_VERSIONS':['v1','v2'], "VERSION_PARAM":'version', }
路由控制
urlpatterns = [ re_path(r'^api/', include('app01.urls')), ] urlpatterns = [ re_path(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'), ]
视图函数
class UsersView(APIView): # versioning_class = ParamVersion def get(self,request,*args,**kwargs): # version = request._request.GET.get('version') 手动获取版本号 # print(version) # version = request.query_params.get('version') #通过request的方法获取版本号 print(request.version) #获取版本 print(request.versioning_scheme) #获取处理版本的对象 u1 = request.versioning_scheme.reverse(viewname='uuu',request=request) #反向生成url print(u1) return HttpResponse('Marvelous')
源码解读
1.请求进来之后,依旧先执行dispatch()方法
2.对request进行封装
3.继续执行initial方法
4.执行determine_version方法
5.返回版本号以及版本处理对象
解析器
demo
源码解读
1.