DRF学习——版本组件

一、简介

本篇文章主要用于介绍drf的版本组件(使用URLPathVersioning)的快速使用以及源码分析

二、快速使用

下面介绍def自带的版本控制组件如何使用

①在settings.py中设置默认组件为URLPathVersioning以及相关配置

# /day14/settings.py
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
    # 将VERSION_PARAM设置为version,如果不设置,drf也默认为version
    "VERSION_PARAM": "version",
    # 默认的版本设置为v4
    "DEFAULT_VERSION": "v4",
    # 允许的版本设置为v1或v2,其中默认的v4也被允许
    "ALLOWED_VERSIONS": ["v1", "v2"]

}

②在views.py中编写一个简单的视图类

# api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response


class HomeView(APIView):
    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response("...")

③在urls.py中编写url

# day14/urls.py
from django.urls import path
from api import views

urlpatterns = [
    # 这里的<str:version>中的version是根据settings中的VERSION_PARAM来确定
    # 如果VERSION_PARAM不是给的默认version,则需要进行调整
    path('home/<str:version>/', views.HomeView.as_view(), name="home-list"),
]

④测试接口

访问默认版本http://127.0.0.1:8000/home/v4/

访问允许版本http://127.0.0.1:8000/home/v1/

访问非法版本 http://127.0.0.1:8000/home/v3/

 

三、源码分析

下面从源码角度分析drf实现版本控制的流程

①找到APIView的dispatch方法,其源码如下:

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

 与版本控制有关的代码为:

self.initial(request, *args, **kwargs)

调用了initial方法,其源码如下:

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        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.version, request.versioning_scheme = version, scheme

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

与版本控制有关的代码为:

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

将determine_version的返回值分别赋给version和scheme,最后在赋值给request.version和request.versioning_scheme

其中determine_version的源码如下:

    def determine_version(self, request, *args, **kwargs):
        """
        If versioning is being used, then determine any API version for the
        incoming request. Returns a two-tuple of (version, versioning_scheme)
        """
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)

首先是判断当前实例化对象的versioning_class是否为空,如果为空则返回两个None,即不做版本控制,如果有versioning_class,则将versioning_class实例化对象赋给scheme,返回versioning_class实例化对象的determine_version返回值和versioning_class实例化对象

那么versioning_class从哪里读取?其在APIView中的定义如下:

versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

即从settings文件中读取,前面我们在settings文件中定义的versioning_class是URLPathVersioning,因此此时需要查看URLPathVersioning的determine_version做了什么,其源码如下:

class URLPathVersioning(BaseVersioning):
    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
        )

找到其中的determine_version

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

将从kwargs中获取version_param和default_version,这俩值在最开始的时候定义在了settings中,分别是"version"和"v4",这里在提一下kwargs是urls传过来的参数即<str:version>,因此此时的version等于前面测试接口时的v1,v3和v4

然后如果判断此时的version是否为空,为空就把v4赋值给它

接下来将version作为参数传入is_allowed_version方法中,其源码为

    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))

首先是判断allowed_versions是否为空,即有没有对版本进行限制,如果为空没有做限制即返回True,因此需要先知道allowed_versions是什么,其定义在APIView中为:

allowed_versions = api_settings.ALLOWED_VERSIONS

即去settings文件里读取ALLOWED_VERSIONS,我们定义的是v1和v2,于是返回的时候,判断此时的version是否是default_version即v4或者是否在allowed_versions中即是否是v1和v2其中一个,满足其中一个即返回True,否则返回Flase,如何回到determine_version,如果返回的是Flase,则报错,返回的是True则将此时从url获得的version返回,赋值给request.version

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hemameba

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值