DRF之版本控制、认证和权限组件

一、版本控制组件

1、为什么要使用版本控制

首先我们开发项目是有多个版本的
当我们项目越来越更新,版本就越来越多,我们不可能新的版本出了,以前旧的版本就不进行维护了
像bootstrap有2、3、4版本的,每个版本都有它对应的url,https://v2.bootcss.com/ 、 https://v3.bootcss.com/
这就需要我们对版本进行控制,这个DRF也给我们提供了一些封装好的版本控制方法

 

2、原理

1.
在DRF框架中,它默认帮我们设置了版本信息在request.versionrequest.versioning_scheme中,
只是DRF默认的版本信息是None,我们需要重写一些配置,让request.version携带上我自定义的版本信息
request.versioning_scheme是实现版本控制的类的实例化对象

 

2.

CBV视图函数都是首先会去执行as_view方法的,as_view会找到路由的分发方法dispatch,在真正分发之前会执行initial方法

 

3、使用

复制代码
0. 在哪里取版本信息
我们的版本类必须重写determine_version这个方法,request.version就是拿它的返回值
在视图中就可以使用request.version取到版本信息


1. 新建一个py文件
我建在utils文件夹下的version.py
在version.py下重写一个MyVersion类
注意:必须重写determine_version这个方法,因为request.version就是拿它的返回值
class MyVersion(object):
    def determine_version(self, request, version):
        print(request)
        print("版本类:" + version)
        return version


2. 去项目的settings配置
# 覆盖DRF原本的配置DEFAULT_VERSIONING_CLASS,让它的值指向我们写的那个类
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion'
}


3.url配置
urlpatterns = [
    url(r'^(?P<version>v\d)/test', views.VersionView.as_view()),
]


4. 在视图中使用
class VersionView(APIView):
    def get(self, request, version):
        print("request版本:" + request.version)  # 可以在request.version获取版本信息
        print("参数版本:" + version)# 也可以在参数中获取版本信息
        print(request.versioning_scheme)
        if request.version == 'v1':
            return Response("v1版本")
        elif request.version == 'v2':
            return Response('v2版本')
        return Response('不存在的版本')


5.请求url
http://127.0.0.1:8000/v1/test
http://127.0.0.1:8000/v2/test
复制代码

 

4、DRF的versioning模块

复制代码
1. 在这里模块内给我们配置了各种获取版本的类

2. versioning模块
复制代码
# 基础类
class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

    def determine_version(self, request, *args, **kwargs):
        msg = '{cls}.determine_version() must be implemented.'
        raise NotImplementedError(msg.format(
            cls=self.__class__.__name__
        ))

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        return _reverse(viewname, args, kwargs, request, format, **extra)

    def is_allowed_version(self, version):
        if not self.allowed_versions:
            return True
        return ((version is not None and version == self.default_version) or
                (version in self.allowed_versions))


# 在请求头中携带版本信息
class AcceptHeaderVersioning(BaseVersioning):
    """
    GET /something/ HTTP/1.1
    Host: example.com
    Accept: application/json; version=1.0
    """


# 在URL中携带版本信息
class URLPathVersioning(BaseVersioning):
    '''
    urlpatterns = [
        url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
        url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
    ]
    '''


# 在namespace中携带版本信息
class NamespaceVersioning(BaseVersioning):
    '''
    # users/urls.py
    urlpatterns = [
        url(r'^/users/$', users_list, name='users-list'),
        url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
    ]

    # urls.py
    urlpatterns = [
        url(r'^v1/', include('users.urls', namespace='v1')),
        url(r'^v2/', include('users.urls', namespace='v2'))
    ]
    '''


# 在域名中携带版本信息
class HostNameVersioning(BaseVersioning):
    """
    GET /something/ HTTP/1.1
    Host: v1.example.com
    Accept: application/json
    """


# 在参数中携带版本信息
class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
version模块的类
复制代码

3. 基础类的三个配置
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion',
    # 默认版本
    'default_version': 'v1',
    # 可用版本
    'allowed_versions': ['v1', 'v2'],
    # 参数
    'version_param': 'version',
}
复制代码
复制代码
4. 使用DRF的版本控制
方法一
1. 在utils文件夹下的version.py继承versioning的类

from rest_framework import versioning

class MyVersion(versioning.URLPathVersioning):
    pass


2. urls.py
urlpatterns = [
    # url(r'^version_demo/', views.VersionView.as_view()),
    url(r'^(?P<version>[v1|v2]+)/version_demo/', views.VersionView.as_view()),
]

3. settings.py
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion',
    # 默认版本
    'default_version': 'v1',
    # 可用版本
    'allowed_versions': ['v1', 'v2'],
    # 参数
    'version_param': 'version',
}

4.在视图中使用需要接收参数version
class VersionView(APIView):
    def get(self, request, version):
        print(request.version)
        print(request.versioning_scheme)
        if request.version == 'v1':
            return Response("v1版本")
        elif request.version == 'v2':
            return Response('v2版本')
        return Response('不存在的版本')

5. 在浏览器中
可以输入:
    v1/version_demo/
    v2/version_demo/


方法二
其他步骤基本一样,只是我们在配置的时候直接指定某个类
1. urls.py
urlpatterns = [
    # url(r'^version_demo/', views.VersionView.as_view()),
    url(r'^(?P<version>[v1|v2]+)/version_demo/', views.VersionView.as_view()),
]


2. settings.py
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',  # 指定URLPathVersioning这个类
    # 默认版本
    'default_version': 'v1',
    # 可用版本
    'allowed_versions': ['v1', 'v2'],
    # 参数
    'version_param': 'version',
}
复制代码

 

二、认证

1、为什么要认证

我们可以在网站上登录,然后可以有个人中心,对自己信息就行修改
但是我们每次给服务器发请求,由于Http的无状态,导致我们每次都是新的请求
那么服务端需要对每次来的请求进行认证,看用户是否登录,以及登录用户是谁
那么我们服务器对每个请求进行认证的时候,不可能在每个视图函数中都写认证
一定是把认证逻辑抽离出来,以前我们可能会加装饰器,或者中间件,DRF框架也有它的认证

 

2、原理

在前端进行登录的时候,用户名和密码输入正确后,后端根据用户名和密码拿到这个用户的一些信息(用户名,性别等),
把这些信息加密后生成一个随机字符串token返回给前端,
前端下次请求再来的时候,带上这个随机字符串,后端根据自己的解密算法再算出来,实现认证。

 

3、原理图解

 

4、token认证

复制代码
0. 在哪里取认证的信息
我们的认证类必须重写authenticate这个方法,返回值是元组
第一个值赋值给了request.user, 第二个赋值给request.auth
在视图中就可以使用request.user和request.auth取到认证的信息


1. models.py
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    # 用UUID字符串存随机字符串
    token = models.UUIDField(null=True, blank=True)
    CHOICES = ((1, "vip"), (2, "普通用户"), (3, "vvip"))


2. settings配置
# 跟版本一样,覆盖原本的配置,使用自己的配置
REST_FRAMEWORK = {
    # 配置认证类
    # 这样配是全局,所有路由都要认证
    'DEFAULT_AUTHENTICATION_CLASSES':['utils.auth.MyAuth', ]
}


3. utils/auth.py
from rest_framework import authentication
from AuthDemo.models import User
from rest_framework.exceptions import AuthenticationFailed

class MyAuth(authentication.BaseAuthentication):
    # 必须重写authenticate这个方法,返回值是元组
    # 第一个值赋值给了request.user, 第二个赋值给request.auth
    def authenticate(self, request):
        # 获取前端携带的token,看token是否合法
        token = request.query_params.get("token", "")
        if not token:
            raise AuthenticationFailed("没有携带token")
        user_obj = User.objects.filter(token=token).first()
        # 返回需要的信息
        if user_obj:
            return (user_obj, token)
        # 验证不通过,抛出异常
        raise AuthenticationFailed("token不合法")


4. 视图
# 登录后给用户设置uuid
import uuid
class LoginView(APIView):
    def post(self, request):
        name = request.data.get("name", "")
        pwd = request.data.get("pwd", "")
        user_obj = User.objects.filter(name=name, pwd=pwd).first()
        if user_obj:
            # 登录成功,创建一个token返回给前端
            token = uuid.uuid4()
            user_obj.token = token
            user_obj.save()
            return Response(token)
        return Response("用户名或密码不正确")

# 登录成功后,可在request里面取值
class TestView(APIView):
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("登陆后发送的数据")

5. 局部配置
1. 如果只是某些路由需要认证,那么可以局部设置认证,包括版本也可以局部认证

2. 把settings的配置注释掉

3. 在需要认证的视图中声明
from utils.auth import MyAuth

class TestView(APIView):
    authentication_classes = [MyAuth, ]  # 局部配置认证组件
    # versioning_class = xxx  # 局部配置版本组件
    def get(self, request):
        print(request.user)
        print(request.auth)
        return Response("登陆后发送的数据")

6. DRF提供的认证模块
from rest_framework import authentication
一般来说我们做认证不会使用DRF默认的认证类,只会继承它认证的基础类,
然后自己重写一个认证类,或者使用插件进行认证。
复制代码

 

三、权限

1、权限是什么

权限一般也跟用户认证相关联,因为认证后,才知道我们的账号是什么角色,
比如博客、论坛数据库之类的,听说过管理员这个角色吧,
我们申请博客的时候就是向管理员申请,也就是说管理员会有一些特殊的权利,而我们没有。
这些对某件事情决策的范围和程度,我们叫做权限,权限是我们在项目开发中非常常用到的。

 

2、图解原理

 

3、使用

复制代码
0. 如何表示你有权限
我们的权限类必须重写has_permission方法,和一个错误信息message,
根据登录后的用户的类型(认证后用户有自己的用户类型)决定是否给这个用户操作某些事情。


1. models
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    token = models.UUIDField(null=True, blank=True)
    CHOICES = ((1, "vip"), (2, "普通用户"), (3, "vvip"))
    type = models.IntegerField(choices=CHOICES, default=2)  # 用户类型


2. utils/permission.py
from rest_framework import permissions


class MyPermission(permissions.BasePermission):
    message = '权限不足'

    def has_permission(self, request, view):
        # 判断是什么用户
        if request.user.type in [1, 3]:
            return True
        return False


3. 视图
class PermissionView(APIView):
    # 这个接口普通用户不能进入
    permission_classes = [MyPermission, ]  # 局部设置
    authentication_classes = [MyAuth, ]
    def get(self, request):
        return Response('权限测试接口')



4. DRF提供的权限模块
from rest_framework import permissions

可根据时间需求使用
复制代码

 

四、小结

1、如何使用自定义的类实现各种组件的功能

你能大概把源码里面组件的实现流程走通了,你就会知道自定义的类需要重写什么方法,返回值是什么,
或者百度到使用方法,硬把使用流程记下来。

 

2、一些小知识

复制代码
1. 在DRF的接口中,一般是api_settings.xxx的,说明这个变量是默认配置项,你可以在你的settings里面通过重写REST_FRAMEWORK这个字典把它覆盖掉,
但是在settings里面配置的都是影响全局的,就是说所有视图都要遵守的,
如果想要局部设置,那么就在需要设置的视图里面配置相应的设置。
例如:

# 全局配置
REST_FRAMEWORK = {
    # 配置版本控制类
    'DEFAULT_VERSIONING_CLASS': 'utils.version.MyVersion',
    # 默认版本
    'default_version': 'v1',
    # 可用版本
    'allowed_versions': ['v1', 'v2'],
    # 参数
    'version_param': 'version',

    # 配置认证类
    'DEFAULT_AUTHENTICATION_CLASSES':['utils.auth.MyAuth', ]  # 这样配是全局

    # 权限类
    'DEFAULT_PERMISSION_CLASSES': ['utils.permission.MyPermission', ]
}


# 局部设置
class xxx(APIView):
    versioning_class = MyVersion
    permission_classes = [MyPermission, ]
    authentication_classes = [MyAuth, ]
    def get(self, request):
        return Response('xxx')


2. APIView的各种组件默认值
class APIView(View):

    # The following policies may be set at either globally, or per-view.
    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
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS


3. 注意
一般我们自定义的类都会继承DRF框架提供的对应的Base类
可以不继承的,但是继承的好处是,我们知道了要重写的方法,继承后,你重写方法时会有提示
复制代码

 

posted @   我用python写Bug  阅读(513)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示