drf(五)—版本控制

drf(五)—版本控制

1.源码流程

与前几节的介绍相同源码入口依旧为dispatch()inital();

def initial(self, request, *args, **kwargs):
    self.format_kwarg = self.get_format_suffix(**kwargs)
    # Perform content negotiation and store the accepted info on the request
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg
    # Determine the API version, if versioning is in use.
    version, scheme = self.determine_version(request, *args, **kwargs)
    # 猜测可能是版本控制,进入查看。
    
    # 将版本赋值给request对象。
    request.version, request.versioning_scheme = version, scheme
    # 因此,当我们想要获取版本的时候应该可以直接去request中进行查找

    # Ensure that the incoming request is permitted
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)

determine_version() 函数。

def determine_version(self, request, *args, **kwargs):
    if self.versioning_class is None: 
        return (None, None)#版本控制类不存在,返回值为None
    scheme = self.versioning_class() # 实例化版本控制类的对象。
    return (scheme.determine_version(request, *args, **kwargs), scheme)
	# 返回值是两个对象,一个是执行函数,另一个是控制类的实例化对象
    '''
    	版本控制类中使用的不是列表生成式,表明版本控制类只有一个而不是多个。
 		此处表明版本控制类中需要具备determine_version()方法。
 		
 		结合上面函数猜想,返回值应该是版本与对象.
 		versioning_class指向配置文件。
    '''

image-20220407203606146

2.自定义使用

class ParamVersion(object):
    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get('version')
        return version

自定义的类中直接获取参数;

from app01.utils.version import ParamVersion
class VersionView(APIView):
    throttle_classes = []
    permission_classes = []
    authentication_classes = [] #为方便验证直接将认证功能在该函数中取消。
    versioning_class = ParamVersion #版本控制类不能使用列表,因此直接使用类名
    def get(self,request,*args,**kwargs):
        version=request.version
        print(version)
        return JsonResponse({"msg":"当前版本是%s"%(version)})

image-20220407211241476

此种方式使用较少,因为版本号通常是写在路由中而不是以参数的形式进行传播。

3.内置版本控制类

from rest_framework.versioning import URLPathVersioning,BaseVersioning

BaseVersioning类

class BaseVersioning:
    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 from rest_framework.versioning import URLPathVersioningself.allowed_versions:
            return True
        return ((version is not None and version == self.default_version) or
                (version in self.allowed_versions))

URLPathVersioning 类

class URLPathVersioning(BaseVersioning):# 继承上述的类
        """
    To the client this is the same style as `NamespaceVersioning`.
    The difference is in the backend - this implementation uses
    Django's URL keyword arguments to determine the version.

    An example URL conf for two views that accept two different versions.

    urlpatterns = [
    	re_path(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
    	# 使用动态路由进行传参。
        re_path(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
    ]

    GET /1.0/something/ HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    
    
    
    invalid_version_message = _('Invalid version in URL path.')
	
    # 重写该方法。
    def determine_version(self, request, *args, **kwargs):
        version = kwargs.get(self.version_param, self.default_version)
        if version is None:
            version = self.default_version

        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version
	# 路由解析,使用较少
    def reverse(self,viewname,args=None,kwargs=None, request=None, format=None, **extra):
        if request.version is not None:
            kwargs = {} if (kwargs is None) else kwargs
            kwargs[self.version_param] = request.version

        return super().reverse(
            viewname, args, kwargs, request, format, **extra
        )

内置类封装的功能已经可以满足大多数功能。

全局配置:

image-20220407214230372

默认版本号,允许的版本号,版本参数名称。

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.MyAuthentication',],
    "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
    "UNAUTHENTICATED_TOKEN":None,
    "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.MyPermission',],
    "DEFAULT_THROTTLE_CLASSES":['app01.utils.throttle.MyThrottle',],
    	# 匿名用户不能在全局配置需要为登录功能单独添加
    "DEFAULT_THROTTLE_RATES":{
        "visit":'3/m',#一分钟三次,匿名用户
        "loginuser":'10/m',# 登录成功,一分钟10次
    },
    
   # 版本的配置直接配置即可使用内置的版本控制类。
   "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    "DEFAULT_VERSION":'v1',
    "ALLOWED_VERSIONS":['v1','v2'], #允许的版本号
    "VERSION_PARAM":"version",# 这个参数应该和 路由中的名称相同version/
}

image-20220407214953729

继续努力,终成大器!

posted @ 2022-04-07 21:52  紫青宝剑  阅读(93)  评论(0编辑  收藏  举报