Django restframework api版本控制組件增加及源碼分析

来源:https://www.cnblogs.com/aitiknowledge/archive/2020/06/08/13067643.html
-Advertisement-
Play Games

註意:一定要跟著博主的解說再看代碼的中文註釋及其下麵的一行代碼!!! 說到api版本控制,就是我們的前端人員請求的後臺介面可能有多個版本,後臺的介面地址一般是有兩種形式,博主現以這兩種形式逐一解釋api版本控制組件的源碼剖析。 第一種api版本控制的url格式一般是:http://localhost ...


註意:一定要跟著博主的解說再看代碼的中文註釋及其下麵的一行代碼!!!

說到api版本控制,就是我們的前端人員請求的後臺介面可能有多個版本,後臺的介面地址一般是有兩種形式,博主現以這兩種形式逐一解釋api版本控制組件的源碼剖析。

第一種api版本控制的url格式一般是:http://localhost:8000/user/select/?version=v1。第二種是:http://localhost:8000/user/v1/select/。分別對應以下兩種

1、我們依然是使用流程來解析源碼,首先我們肯定是匹配user下select路由的視圖類進入as_view方法

from django.conf.urls import url

from . import views


app_name = '[user]'
urlpatterns = [
    # 這是get請求參數的
    url(r'select/', views.UserView.as_view(), name="select"),   # 用戶信息查詢所有

    # 這是urlpath路徑的參數
    url(r'^?P<version>[v1|v2])/select/$', views.UserView.as_view(), name="select"),   # 用戶信息查詢所有,與上者只存其一

]

 

2、在看下UserView下有沒有as_view方法

class UserView(APIView):

    def get(self, request):
        print(request.version)
        return Response(data={"code": 200, "result": "res"})

3、會發現UserView下沒有as_view方法,這個是可以查看APIView父類下有沒有as_view方法,若沒有,就以此類推。顯而易見,APIView下有as_view方法,博主添上代碼已中文註釋

    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        # 執行父類的as_view方法,這裡的cls就是請求視圖類
        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)
APIView.as_view()

4、但是APIView類的as_view方法中又去執行了父類的as_view方法,super關鍵字已經在前幾篇博客中提及,朋友們可前往查看。博主就再一次添上View父類的as_view方法代碼及中文註釋

    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        # 執行view方法
        def view(request, *args, **kwargs):
            # 實例化請求視圖類對象,即self是請求視圖類對象
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            # 註意:這裡的request對象還是原生的request對象
            self.request = request
            self.args = args
            self.kwargs = kwargs
            # dispatch方法至關重要
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view
View.as_view()

5、在上面的方法中他就會執行到裡面的view方法,根據中文註釋,最後再一次從請求視圖類開始尋找dispatch方法,請求視圖類中沒有,就又回到了APIView類中,執行dispatch方法,添上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還是請求視圖類對象
        self.args = args
        self.kwargs = kwargs
        # 這裡是對原生的request加工處理,返回一個新的request對象
        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:
                # 通過python的反射機制反射到請求視圖類的方法名稱
                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
APIView.dispatch()

6、api版本控制組件就在initial方法裡頭,我們點進去看下,添上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.
        # rest framework的api的版本控制
        # 類似於:api/?version=v1  或者是   api/v1/
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        # 查看源碼,用戶驗證的方法,這個request 是加工之後的request
        self.perform_authentication(request)
        # 用戶許可權驗證
        self.check_permissions(request)
        # 用戶訪問頻率限制
        self.check_throttles(request)
APIView.initial()

7、裡面就會有版本控制的代碼語句:version, schema = self.determine_version(request, *args, **kwargs),接下來就添上涉及determine_version方法的一些重要代碼及中文註釋

class APIView(View):
    # 如果請求視圖類中沒有versioning_class變數及值,就會去匹配全局配置文件的值
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    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)
        """
        # 這裡判斷self.versioning_class是否有值
        if self.versioning_class is None:
            return (None, None)
        # 實例化版本控制類對象
        scheme = self.versioning_class()
        from rest_framework.versioning import BaseVersioning
        # 這裡就對應上一個方法的版本值和調用的版本控制類對象
        return (scheme.determine_version(request, *args, **kwargs), scheme)
APIView.determine_version()

8、從上面就可以看出我們可以在請求視圖類中編寫上面提及的變數,但是這個變數的值就不是列表了,還有代碼中的註釋,值可以是一個我們自定義或者框架自帶的版本控制類,隨後添上Django框架中versioning.py文件的部分版本控制類

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 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):
        # 獲取版本值
        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

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
        # 這個方法就是生成反向的url,這裡傳遞視圖的名稱之外,只需要傳遞新封裝的request對象即可
        url = super(QueryParameterVersioning, self).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 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.
    # 編寫url路由的規則就需要遵循下麵的編寫規則,這個很重要
    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')
    ]

    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 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):
        # 這個方法就是生成反向的url,這裡傳遞視圖的名稱之外,只需要傳遞新封裝的request對象即可
        if request.version is not None:
            kwargs = {} if (kwargs is None) else kwargs
            kwargs[self.version_param] = request.version

        return super(URLPathVersioning, self).reverse(
            viewname, args, kwargs, request, format, **extra
        )
versioning.py

9、最後,我們若想要自定義,就需要繼承裡面的基類,就和上面的類似,而且其實我們是不需要自定義,因為Django中已經夠開發使用。如果在全局中配置,方式如下:

REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS": "URLPathVersioning",
        "DEFAULT_VERSION": "v1",   # 預設的版本
        "ALLOWED_VERSIONS": ["v1", "v2"]   # 允許的版本
    }

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jQuery是我們在WEB開發中使用最多的JS類庫,他可以很方便的幫助我們實現很多絢麗的功能。本文將給大家介紹JQuery的一種小功能,用來禁用文本框的剪切、複製、粘貼功能,完整的頁面代碼如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...
  • 第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分散式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ...
  • JVM JVM->Java Virtual Machine:Java虛擬機,是一種用於計算設備的規範,它是一個虛構出來的電腦,是通過在實際的電腦上模擬模擬各種電腦功能來實現的。 基本認知: 1.JVM是用於運行Java代碼的假象電腦,主要有一套位元組碼指令集,一組寄存器,一個棧,一個垃圾回收, ...
  • 從零開始Hibernate實戰篇:(四篇實戰代碼,循序漸進,註意事項和學習內容在代碼註釋裡面,面向有一定基礎的同學使用) 第一篇:基礎篇(搭建) Hibernate開發基本步驟: 1.導入jar包(hibernate-release-5.0.7.Final\lib的文件裡面去找所需要的jar包) 2 ...
  • 很多人接觸Python,都是從爬蟲開始,其實很多語言都可以做爬蟲,只是Python相對其他語言來說,更加簡單而已。但是Python並不止於爬蟲,在人工智慧,科學計算等方面的應用更加廣泛。古人雲:萬丈高樓平地起,要想有長足的發展,打好基礎很重要,本文主要講解Python的面向對象相關知識,僅供學習分享... ...
  • 1、問題描述 安裝在docker容器裡面的storage一直處於退出狀態,導致文件無法存儲。 2、解決方案 查看docker中安裝的容器 docker ps -a 嘗試啟動容器 docker start storage (或者 docker start "容器ID") 若嘗試啟動容器後,容器仍處於退 ...
  • 1 from datetime import * 2 today=datetime.today() 3 now=datetime.now() 4 5 #判斷今天是星期幾 6 today.isoweekday() 7 8 #計算一周以後是星期幾 9 day=timedelta(days=7) 10 t ...
  • 上一節給大家分享了掃雷游戲的源代碼,本篇文章當然也不會讓大家失望,專門針對C語言入門或者學習了部分知識之後的小伙伴來練手的游戲項目——《五子棋大戰》,本期並不是使用的easyX,因為考慮到有些大學的同學沒有接觸到這個,所以本期就是一個“黑漆漆”的控制台界面,這個就希望大家諒解哈!,話不多說,下麵我們 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...