DRF-django_rest_framework版本控制基本使用及源码剖析

83 阅读3分钟

版本控制

from rest_framework.versioning import QueryParameterVersioning
class user(APIView):
    versioning_class = QueryParameterVersioning
    def get(self,request):
        # print(request.META)        
        return Response({'coed':123})
 
class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    invalid_version_message = _('Invalid version in query parameter.')

    def determine_version(self, request, *args, **kwargs):
        #request.query_params.get会去url中读取self.version_param,如果没设置,就会读取默认的default_version,url中没有传递参数的话version=self.default_version
        version = request.query_params.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version
    
class BaseVersioning:
    default_version = api_settings.DEFAULT_VERSION		#设置默认的版本
    allowed_versions = api_settings.ALLOWED_VERSIONS	#可以设置允许的版本
    version_param = api_settings.VERSION_PARAM	    #可以自己设置url中版本的键

修改读取版本信息的全局设置

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER':None,
    
    #读取版本的时候会读取version这个键的值
    'VERSION_PARAM':'version'
    
    #如果没有传递版本信息,默认是v1
    'DEFAULT_VERSION':'v1'
	
    #允许的版本
    'ALLOWED_VERSIONS':['v1','v2','v3']
}

源码解读

在dispatch中的initial:

    def initial(self, request, *args, **kwargs):
        self.format_kwarg = self.get_format_suffix(**kwargs)
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        ## 1 ##通过self.determine_version获取version,此时会在QueryParameterVersioning
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
        
    def determine_version(self, request, *args, **kwargs):
		## 2 ##获取我们定义的versioning_class并实例化,执行determine_version方法获取版本并返回
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        
        #注意这里是返回的第一个是版本,第二个返回值是版本的对象
        return (scheme.determine_version(request, *args, **kwargs), scheme)
		
        
class QueryParameterVersioning(BaseVersioning):
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """
    invalid_version_message = _('Invalid version in query parameter.')

    #用于提取url中的版本的值
    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get(self.version_param, self.default_version)
        #判断请求的版本是否合法,is_allowed_version方法在父类中
        if not self.is_allowed_version(version):
            #不合法就触发异常,会被dispatch捕获,因为是dispatch调用的inital
            raise exceptions.NotFound(self.invalid_version_message)
        return version
	
    #反向生成url
    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        #调用父类的reverse
        url = super().reverse(
            viewname, args, kwargs, request, format, **extra
        )
        
        #判断请求的版本是否合法
        if request.version is not None:
            return replace_query_param(url, self.version_param, request.version)
        return url
    
class BaseVersioning:
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

    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))
    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        #调用_reverse
        return _reverse(viewname, args, kwargs, request, format, **extra)
    
    
    def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra):
    """
    Same as `django.urls.reverse`, but optionally takes a request
    and returns a fully qualified URL, using the request to get the base URL.
    """
    if format is not None:
        kwargs = kwargs or {}
        kwargs['format'] = format
        
    #实际上是调用django的reverse
    url = django_reverse(viewname, args=args, kwargs=kwargs, **extra)
    if request:
        return request.build_absolute_uri(url)
    return url

请求不合法的版本

'''GET /user/?version=v4'''

HTTP 404 Not Found
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "detail": "Invalid version in query parameter."
}

反向生成url

'''
urlpatterns = [
    # path("admin/", admin.site.urls),
    path("user/", views.user.as_view(),name='na'),

]
'''

url = request.versioning_scheme.reverse('na',request=request)
print('反向生成url:',url)

--反向生成url: http://127.0.0.1:8000/user/?version=v3

版本参数传递方法2:放在url中

    path("api/<str:version>/index", views.index.as_view(), name='no'),

    #请求的url:     http://127.0.0.1:8000/api/v1/index
    
    #views.py
    from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning

    class index(APIView):
    versioning_class = URLPathVersioning
    def get(self,request,*args,**kwargs):
        url = request.versioning_scheme.reverse('no',request=request)
        print(url)
        return Response({'asd':123})

版本参数传递方法3:在请求头中

#请求的键和值 Accept: application/json; version=v1   

class headversion(APIView):
    versioning_class = AcceptHeaderVersioning
    def get(self,request):
        url = request.versioning_scheme.reverse('ne',request=request)
        print(url)
        return Response({'asd':12})

设置全局读取版本路径

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER':None,
    'VERSION_PARAM':'version',
    'DEFAULT_VERSION':'v1',
    'ALLOWED_VERSIONS':['v1','v2','v3'],
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning'

}