DRF之版本控制、认证和权限组件
一、版本控制组件
1、为什么要使用版本控制
首先我们开发项目是有多个版本的
当我们项目越来越更新,版本就越来越多,我们不可能新的版本出了,以前旧的版本就不进行维护了
像bootstrap有2、3、4版本的,每个版本都有它对应的url,https://v2.bootcss.com/ 、 https://v3.bootcss.com/
这就需要我们对版本进行控制,这个DRF也给我们提供了一些封装好的版本控制方法
2、原理
1.
在DRF框架中,它默认帮我们设置了版本信息在request.version和request.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 """
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类 可以不继承的,但是继承的好处是,我们知道了要重写的方法,继承后,你重写方法时会有提示
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· 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