DRF版本控制

  • 介绍

我们在看APIView源码时可以看到,版本和版本控制类是通过determine_version的返回值获取的

version, scheme = self.determine_version(request, *args, **kwargs)

并且将版本和版本控制类放入了request中,request.versioning_scheme就是控制类的实例化对象

request.version, request.versioning_scheme = version, scheme

所以视图中获取版本就需要用request.version

# 下面是rest_framework.versioning里所有的版本控制方法

# 最基础的版本控制类, 其他类继承并重写determine_version方法
class BaseVersioning(object):pass # 在accept请求头中配置版本信息,Accept: application/json; version=1.0
class AcceptHeaderVersioning(BaseVersioning):pass # 在url上携带版本信息,url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
class URLPathVersioning(BaseVersioning):pass # 把版本信息放在路由分发中, 把namespaces配置成版本,url(r'^v1/', include('users.urls', namespace='v1')
class NamespaceVersioning(BaseVersioning):pass # 在host上配置版本信息, Host: v1.example.com
class HostNameVersioning(BaseVersioning):pass # 在url过滤条件上配置版本信息,GET /something/?version=0.1 HTTP/1.1
class QueryParameterVersioning(BaseVersioning):pass
  • 使用

以上面通过url携带版本信息为例进行版本控制使用,

# 在settings下进行版本信息配置
REST_FRAMEWORK = {
# 默认使用的版本控制类,这里使用url携带版本信息
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
# 允许的版本
'ALLOWED_VERSIONS': ['v1', 'v2'],
# 版本使用的参数名称, 版本的参数名与urls中 ?P<version>中名称一致
'VERSION_PARAM': 'version',
# 默认使用的版本
'DEFAULT_VERSION': 'v1',
} # 在urls中配置如下
urlpatterns = [
re_path(r'^(?P<version>[v1|v2]+)/demo', VersionView.as_view()),
] # 视图类中使用如下
class VersionView(APIView):
def get(self, request, *args, **kwargs):
# request.version获取版本
versions = request.version
if versions == "v1":
return Response("版本1的信息")
elif versions == "v2":
return Response("版本2的信息")
else:
return Response("无此版本")

DRF认证

  • 说明

在写认证前我们需要去看下源码,看下DRF是怎么做的,在initial方法里面有self.perform_authentication(request),这个就是认证,再深入查找源码,发现下面这句话,

for authenticator in self.authenticators:

  ser_auth_tuple = authenticator.authenticate(self)

self.authenticators是一个认证类的实例化列表,这里是循环调用每一个实例化中的authenticate方法,并传入了self,这里的self指的就是request,因此我们如果要使用自定义认证就必须重新authenticate的方法.

  • 使用

准备基本工作:

# 在models下创建一张用户表,登录成功后可以保存token
# 在models下创建一张用户表,登录成功后可以保存token
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
token = models.UUIDField(null=True, blank=True) # 在视图中编写登录,登录成功则插入token
class LoginView(APIView):
def post(self, request):
user = request.data.get('user')
pwd = request.data.get('pwd')
# 检验用户名和密码是否正确
user_obj = User.objects.filter(username=user, password=pwd).first()
if user_obj:
# 生成uuid并赋值给token,保存至数据库
user_obj.token = uuid.uuid4()
user_obj.save()
return Response(user_obj.token)
return Response("用户名或密码错误")

准备工作完成后,我们就可以编写自定义认证类来进行认证

单独新建一个auth.py文件

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication from .models import User # 必须继承BaseAuthentication并重写authenticate方法
class MyAuth(BaseAuthentication):
def authenticate(self, request):
# 假设前端是通过请求参数传递过来的
token = request.query_params.get("token", "")
if not token:
# 认证失败就抛出该异常
raise AuthenticationFailed({"code":1001, "error_msg": "缺少token"})
user_obj = User.objects.filter(token=token).first()
if not user_obj:
raise AuthenticationFailed({"code": 1001, "error_msg": "无效的token"})
# 成功则返回一个元组,从源码中user_auth_tuple = authenticator.authenticate(self)
# 然后self.user, self.auth = user_auth_tuple,这两句可以知道返回元组并赋值
return (user_obj, token)

然后写一个index页面,进行访问,访问前需要认证,下面属于局部视图认证

class IndexView(APIView):
# 如果没有配置全局认证,可以单独指定首页需要经过自定义的认证
authentication_classes = [MyAuth, ]
def get(self, request):
return Response("首页")

当然如果需要所有的都要通过自定义的认证,可以在settings下面进行配置,下面属于全局注册:

REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': "v1",
'ALLOWED_VERSIONS': ["v1", "v2"],
'VERSION_PARAM': 'version',
# 配置全局认证
'DEFAULT_AUTHENTICATION_CLASSES': ["authTest.auth.MyAuth", ]
}

DRF权限

  • 说明

还是老步骤,在写之前我们需要去看下源码,看下DRF是怎么做的,在initial方法里面有self.check_permissions(request),这个就是权限,再深入查找源码,发现下面这句话,

for permission in self.get_permissions():

  if not permission.has_permission(request, self):

    self.permission_denied(

      request, message=getattr(permission, 'message', None)

    )

self.get_permissions返回的是一个权限实例化列表,这里是循环调用每一个实例化中的has_permission方法,并传入了request和self,这里的self指的是我们视图对象.self.permission_denied则是抛出异常,错误信息则是message

  • 使用

准备基本工作,在认证表的基础上,加一个权限字段:

role = models.IntegerField(choices=((1, '普通用户'),(2, 'vip用户'), (3, '管理员')), default=1)

新建一个permissions.py文件

from rest_framework.permissions import BasePermission

class MyPermission(BasePermission):
message = "无此权限访问" def has_permission(self, request, view):
"""自定义该权限只有管理员才有
注意我们初始化时候的顺序是认证在权限前面的,所以只要认证通过~
我们这里就可以通过request.user,拿到我们用户对象
"""
if request.auth and request.user.role == 3:
return True
else:
return False
class PermissionView(APIView):
# 经过自定义认证
authentication_classes = [MyAuth, ]
# 经过自定义权限
permission_classes = [MyPermission] def get(self, request):
return Response("管理员才能看到")

上面是局部组件配置,settings中可以全局配置

REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': "v1",
'ALLOWED_VERSIONS': ["v1", "v2"],
'VERSION_PARAM': 'version',
# 配置全局认证
# 'DEFAULT_AUTHENTICATION_CLASSES': ["authTest.auth.MyAuth", ],
# 配置全局权限
# "DEFAULT_PERMISSION_CLASSES": ["authTest.permissions.MyPermission"]
}

DRF频率

  • 说明

依旧先去看源码,实际上版本,认证,权限,频率都在initial方法里面,self.check_throttles这个就是频率组件,

for throttle in self.get_throttles():

    if not throttle.allow_request(request, self):

      throttle_durations.append(throttle.wait())

看了那么多遍,一看就知道self.get_throttles()肯定返回的是频率类实例化的一个列表,然后调用allow_request方法,也就是说我们要是自定义的话也必须要写该方法,不然肯定报错,这里还有个throttle.wait(),所以还得写一个无参的wait方法.

  • 自定义频率

    新建throttles.py文件,编写MyThrottle类

    from rest_framework.throttling import BaseThrottle
    
    import time
    
    VISIT_USER = {}
    # 一分钟限制访问5次
    class MyThrottle(BaseThrottle):
    """自定义频率类""" def __init__(self):
    self.history = None def allow_request(self, request, view):
    """必须重写该方法
    实现思路:
    1. 通过ip进行限制,所以先获取ip
    2. 以ip为key,讲每次访问的时间计入到value中,value为列表
    3. 访问时间列表最小和最大值相差1分钟以上的进行循环剔除,只留下有效的时间
    4. 判断访问次数是否超过了5次,也就是时间列表长度是否大于5
    """
    # 1.先获取访问用户的ip地址
    remote_addr = request.META.get('REMOTE_ADDR')
    # 2.判断ip是否在字典中,也就是判断用户是否第一次访问
    now = time.time() # 获取当前时间戳
    if remote_addr not in VISIT_USER:
    VISIT_USER[remote_addr] = [now]
    # 从源码中可以知道,返回真则是不限制,因为才第一次所以不限制
    return True
    # 3.走下面逻辑证明用户不是第一次访问,所以先取出当前访问的ip时间列表
    history = VISIT_USER[remote_addr]
    # 4.插入用户最新访问的时间
    history.insert(0, now)
    # 5.循环时间列表,剔除最新最老时间差大于一分钟的记录
    while history[0] - history[-1] > 60:
    history.pop()
    # 保存最新的列表数据到self.history,因为wait方法中需要
    self.history = history
    # 6.判断时间列表长度是否大于最大允许的次数
    if len(history) > 5:
    return False
    else:
    return True def wait(self):
    """返回需要多久时间才能再次访问"""
    rest_time = 60 - (self.history[0] - self.history[-1])
    return rest_time

    在视图中可以进行配置throttle_classes = [MyThrottle]:

    class PermissionView(APIView):
    # 经过自定义认证
    authentication_classes = [MyAuth, ]
    # 经过自定义权限
    permission_classes = [MyPermission]
    # 自定义频率访问次数
    throttle_classes = [MyThrottle] def get(self, request):
    return Response("管理员才能看到")
  • 使用DRF自带的频率组件

使用之前,必须先看下源码,看下需要怎么使用,from rest_framework import throttling,进入throttling查看SimpleRateThrottle,首先代码肯定先走allow_request,首先有self.rate,而这个又是self.get_rate()的返回值,该方法又要去拿scope属性的值,没有则抛异常,所以我们需要定义scope,从下面两行代码我们看下:

return self.THROTTLE_RATES[self.scope] THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES

从上面可以看到scope的值是DEFAULT_THROTTLE_RATES字典中的key,目的就是要取出value值,再来进一步看,该值返回给的是self.rate,而在初始化的时候源码如下:

self.num_requests, self.duration = self.parse_rate(self.rate)

我们再来看下parse_rate方法干了啥,因为接收了DEFAULT_THROTTLE_RATES的value值,我们得去看下该值怎么配置才行,从源码中得知,我们需要 以 次数/单位时间的方式配置,所以我们可以配置5/m,也就是每分钟五次,在settings里面配置如下:,这里把所有配置都写上去了

REST_FRAMEWORK = {
# 版本控制
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': "v1",
'ALLOWED_VERSIONS': ["v1", "v2"],
'VERSION_PARAM': 'version',
# 配置全局认证
'DEFAULT_AUTHENTICATION_CLASSES': ["authTest.auth.MyAuth", ],
# 配置全局权限
"DEFAULT_PERMISSION_CLASSES": ["authTest.permissions.MyPermission"],
# 频率限制的配置
"DEFAULT_THROTTLE_CLASSES": ["authTest.throttles.MyThrottle"],
"DEFAULT_THROTTLE_RATES":{
'Num':'5/m', #速率配置每分钟不能超过5次访问,Num是scope定义的值,
}
}

虽然做了配置,但是我们继承SimpleRateThrottle也没有写方法啊,那它怎么知道我们用什么做限制呢?所以继续看源码,我们看到要执行get_cache_key方法,而get_cache_key需要被重新,不然就报错,所以我们需要重写,那我们要写啥呢?结合我们自定义的实现和源码中可以看出get_cache_key方法的返回值应该就是用户访问的ip地址,而且我们从BaseThrottle的get_ident方法可以知道,该方法就是返回ip地址的.所以我们就可以直接写了,见下图:

class DRFThrottle(SimpleRateThrottle):
scope = "Num" # 定义key值,从settings中通过该key取值 def get_cache_key(self, request, view):
return self.get_ident(request)

Django REST Framework(DRF)_第三篇的更多相关文章

  1. Django REST Framework(DRF)_第四篇

    DRF分页(总共三种) PageNumberPagination(指定第n页,每页显示n条数据) 说明 既然要用人家的那么我们就先来看下源码,这个分页类源码中举例通过参数指定第几页和每页显示的数据:h ...

  2. Django REST Framework(DRF)_第二篇

    视图和路由 视图封装 第一次封装 ​ 上一篇最后我们对书籍表做了增删改查,那么如果现在我们有几十上百张表需要这样做呢?我们知道类的特性有封装,因此我们可以尝试进行封装下. from rest_fram ...

  3. Django REST Framework(DRF)_第一篇

    认识RESTful REST是设计风格而不是标准,简单来讲REST规定url是用来唯一定位资源,而http请求方式则用来区分用户行为. REST接口设计规范 HTTP常用动词 GET /books:列 ...

  4. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

  5. Django 学习之Django Rest Framework(DRF)

    一. WEB应用模式 在开发Web应用中,有两种应用模式 1. 前后端不分离 把html模板文件和django的模板语法结合渲染完成以后才从服务器返回给客户. 2. 前后端分离 二. API接口 AP ...

  6. day71:drf:API接口&Restful API规范&Django Rest Framework&drf中的序列化和反序列化功能

    目录 1.web应用模式 2.API接口 3.Restful API规范 4.序列化 5.Django Rest Framework 1.drf的简单介绍 2.drf的特点 3.如何安装drf 4.d ...

  7. python 全栈开发,Day94(Promise,箭头函数,Django REST framework,生成json数据三种方式,serializers,Postman使用,外部python脚本调用django)

    昨日内容回顾 1. 内容回顾 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放数据 修改数据的唯一方式 异步操作 修改state中数据的步骤: ...

  8. DRF Django REST framework 之 序列化(三)

    Django 原生 serializer (序列化) 导入模块 from django.core.serializers import serialize 获取queryset 对queryset进行 ...

  9. Python全栈开发记录_第三篇(linux(ubuntu)的操作)

    该篇幅主要记录linux的操作,常见就不记录了,主要记录一些不太常用.难用或者自己忘记了的点. 看到https://www.cnblogs.com/resn/p/5800922.html这篇幅讲解的不 ...

随机推荐

  1. Docker Windows

    docker windows 操作和linux上稍有些差异,主要是在启动容器时的命令参数 和 端口映射. 1.下载docker-toolbox http://mirrors.aliyun.com/do ...

  2. VS Code中内置终端运行C中文乱码问题

    环境:Win10 + VS Code + Code Runner插件 原因:VS Code默认文件编码为 UTF-8,生成的可执行文件也是UTF-8编码的,但是系统编码为 GB2312,所以程序中的中 ...

  3. 【FFMPEG】使用ffmpeg类库打开流媒体

    版权声明:本文为博主原创文章,未经博主允许不得转载. 使用ffmpeg类库进行开发的时候,打开流媒体(或本地文件)的函数是avformat_open_input(). 其中打开网络流的话,前面要加上函 ...

  4. Navicat Premium12 注册机下载及教程

    1.下载Navicat Premium 官网https://www.navicat.com.cn/下载最新版本下载安装(文末,网盘地址有64位安装包和注册机下载) 2.激活Navicat Premiu ...

  5. ZOJ Problem Set - 1004

    1.翻译参考 http://liucw.blog.51cto.com/6751239/1198026 2.代码参考 http://www.cnblogs.com/devymex/archive/201 ...

  6. 数据结构 双向链表 C语言实现

    dlist.h #ifndef __dList_H #define __dlist_H typedef int Item; typedef struct Node *PNode; typedef PN ...

  7. hdoj1011(树上分组背包)

    题目链接:https://vjudge.net/problem/HDU-1011 题意:给定一颗树,每个结点有两个属性,即花费V和价值w,并且选择子结点时必须选择父结点,求总花费不超过m的最大价值. ...

  8. [转帖]使用Grafana和Telegraf监视VMware ESXi的方法

    使用Grafana和Telegraf监视VMware ESXi的方法 2019-04-03 15:28:30作者:曾秀珠稿源:云网牛站 https://ywnz.com/linuxyffq/4660. ...

  9. Git基本理解

    1.版本控制 Git 是一个分布式版本控制系统 (Distributed Version Control System - DVCS). 所谓版本控制,意思就是在文件的修改历程中保留修改历史,让你可以 ...

  10. 基于Centos 搭建Jenkins环境

    ⒈简介 Jenkins 是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. ⒉Java安装 首先我们需要准备 ...