rest-framework框架——解析器、ur控制、分頁、響應器、渲染器、版本

1、解析器(parser)

  解析器在reqest.data取值的時候才執行。html

  對請求的數據進行解析:是針對請求體進行解析的。表示服務器能夠解析的數據格式的種類。python

from rest_framework.parsers import JSONParser, FormParser, MultiPartParser, FileUploadParser
"""
默認得是 JSONParser FormParser MultiPartParser 
"""
class BookView(APIView):
    # authentication_classes = [TokenAuth, ]
    parser_classes = [FormParser, JSONParser]

    def get(self, request):....

一、django的request類源碼解析

(1)django中發送請求對比

#若是是urlencoding格式發送的數據,在POST裏面有值
Content-Type: application/url-encoding.....
    request.body
    request.POST
    
#若是是發送的json格式數據,在POST裏面是沒有值的,在body裏面有值,可經過decode,而後loads取值        
Content-Type: application/json.....
    request.body
    request.POST

(2)關於decode和encode

  瀏覽器發送過來是字節須要先解碼 ---> decode 如:s=中文數據庫

  若是是在utf8的文件中,該字符串就是utf8編碼,若是是在gb2312的文件中,則其編碼爲gb2312。這種狀況下,要進行編碼轉換,都須要先用 decode方法將其轉換成unicode編碼,再使用encode方法將其轉換成其餘編碼。一般,在沒有指定特定的編碼方式時,都是使用的系統默認編碼建立的代碼文件。 以下: django

  s.decode(utf-8‘).encode(utf-8) json

  decode():是解碼 --->把字節變成字符串      encode()是編碼---->把字符串變成字節後端

(3)查看django中WSGIRequest解析方法

# 1:導入django的類
from django.core.handlers.wsgi import WSGIRequest

# 2:
class WSGIRequest(http.HttpRequest):
    def _get_post(self):
        if not hasattr(self, ‘_post‘):
            self._load_post_and_files()
        return self._post
# 3:self._load_post_and_files()從這裏找到django解析的方法
def _load_post_and_files(self):
        """Populate self._post and self._files if the content-type is a form type"""
        if self.method != ‘POST‘:
            self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
            return
        if self._read_started and not hasattr(self, ‘_body‘):
            self._mark_post_parse_error()
            return

        if self.content_type == ‘multipart/form-data‘:
            if hasattr(self, ‘_body‘):
                # Use already read data
                data = BytesIO(self._body)
            else:
                data = self
            try:
                self._post, self._files = self.parse_file_upload(self.META, data)
            except MultiPartParserError:
                # An error occurred while parsing POST data. Since when
                # formatting the error the request handler might access
                # self.POST, set self._post and self._file to prevent
                # attempts to parse POST data again.
                # Mark that an error occurred. This allows self.__repr__ to
                # be explicit about it instead of simply representing an
                # empty POST
                self._mark_post_parse_error()
                raise
        elif self.content_type == ‘application/x-www-form-urlencoded‘:
            self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
        else:
            self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()

  從if self.content_type == ‘multipart/form-data‘:和 self.content_type == ‘application/x-www-form-urlencoded‘: 能夠知道django只解析urlencoded‘和form-data這兩種類型。api

  所以在django中傳json數據默認是urlencoded解析到request中:body取到json數據時,取到的數據時字節,須要先decode解碼,將字節變成字符串。瀏覽器

request.body.decode("utf8")
json.loads(request.body.decode("utf8"))

  爲了傳輸json數據每次都要decode\loads,比較麻煩所以纔有瞭解析器解決這個問題。服務器

二、rest-framework的request類源碼解析

  在rest-framework中 是以利用Request類進行數據解析。app

1:找到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        #  解析器

2:找api_settings沒有定義找默認
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES

3:.
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

4:DEFAULTS

DEFAULTS = {
    # Base API policies     #  自帶的解析器
    ‘DEFAULT_PARSER_CLASSES‘: (
        ‘rest_framework.parsers.JSONParser‘,    # 僅處理請求頭content-type爲application/json的請求體
        ‘rest_framework.parsers.FormParser‘,    # 僅處理請求頭content-type爲application/x-www-form-urlencoded 的請求體
        ‘rest_framework.parsers.MultiPartParser‘  # 僅處理請求頭content-type爲multipart/form-data的請求體
    ),

  注意除了上面三種以外還有一個專門處理文件上傳:

from rest_framework.parsers import FileUploadParser

三、局部視圖parser

from rest_framework.parsers import JSONParser,FormParser
class PublishViewSet(generics.ListCreateAPIView):
    parser_classes = [FormParser,JSONParser]
    queryset = Publish.objects.all()
    serializer_class = PublshSerializers
    def post(self, request, *args, **kwargs):
        print("request.data",request.data)
        return self.create(request, *args, **kwargs)

四、全局視圖parser

REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
    "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
    "DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
    "DEFAULT_THROTTLE_RATES":{
        "visit_rate":"5/m",
    },
    "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
}

2、url路由控制

  由於咱們使用的是視圖集而不是視圖,咱們能夠經過簡單地將視圖集註冊到router類來爲咱們的API自動生成URL conf。

  一樣,若是咱們須要對API URL有更多的控制,咱們能夠直接使用常規的基於類的視圖,並顯式地編寫URL conf。

  最後,咱們將默認登陸和註銷視圖包含在可瀏覽API中。這是可選的,但若是您的API須要身份驗證,而且但願使用可瀏覽的API,那麼這是有用的。

一、全自動路由示例

from django.contrib import admin

from django.urls import path, re_path, include
from rest_framework import routers
from app01 import views

routers = routers.DefaultRouter()   # 實例化
routers.register("authors", views.AuthorViewSet)  # 註冊某一個視圖

urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    # as_view參數指定什麼請求走什麼方法
    # re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),
    # re_path(r'^authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view({
    #     'get': 'retrieve',
    #     'put': 'update',
    #     'patch': 'partial_update',
    #     'delete': 'destroy'
    # }), name="author_detail"),
    
    # 改寫以下: 
    re_path(r"", include(routers.urls)),

    re_path(r'^login/$', views.LoginView.as_view(), name="login"),
]

  視圖的內容不須要任何變更即生效:

  

  

  

二、DRF路由組件使用

  路由傳參寫的特別多,可是框架將這些也已經封裝好了。

  修改DRFDemo/urls.py文件以下所示:

from django.urls import path, include
from .views import BookView, BookEditView, BookModelViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()   # 路由實例化
# 第一個參數是路由匹配規則,這裏的路由是分發下來的,所以能夠不作設置;第二個參數是視圖
router.register(r"", BookModelViewSet)

urlpatterns = [
    # path('list', BookView.as_view()),   # 查看全部的圖書
    # 注意url中參數命名方式,2.0以前的寫法:'retrieve/(?P<id>\d+)'
    # 2.0以後的寫法:<>內聲明類型,冒號後面跟着關鍵字參數
    # path('retrieve/<int:id>', BookEditView.as_view())   # 單條數據查看

    # path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})),
    # path('retrieve/<int:id>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"}))
]

urlpatterns += router.urls    # router.urls是自動生成帶參數的路由

 

  可是須要自定製的時候仍是須要咱們本身用APIView寫,當不須要那麼多路由的時候,不要用這種路由註冊,不然會對外暴露過多的接口,會存在風險。總之,一切按照業務須要去用。

3、分頁組件(Pagination)

REST框架支持自定義分頁風格,你能夠修改每頁顯示數據集合的最大長度。

分頁連接支持如下兩種方式提供給用戶:

  • 分頁連接是做爲響應內容提供給用戶
  • 分頁連接被包含在響應頭中(Content-Range或者Link)

內建風格使用做爲響應內容提供給用戶。這種風格更容易被使用可瀏覽API的用戶所接受。

  若是使用通用視圖或者視圖集合。系統會自動幫你進行分頁。

  若是使用的是APIView,你就須要本身調用分頁API,確保返回一個分頁後的響應。能夠將pagination_class設置爲None關閉分頁功能。

一、設置分頁風格

  能夠經過設置DEFAULT_PAGINATION_CLASS和PAGE_SIZE,設置全局變量。

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

  須要同時設置pagination class和page size。 也能夠在單個視圖中設置pagination_class屬性,但通常你須要使用統一的分頁風格。

二、修改分頁風格

  若是你須要修改分頁風格 ,須要重寫分頁類,並設置你須要修改的屬性。

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer import *
from .utils import *
#分頁
from rest_framework.pagination import PageNumberPagination

class MyPageNumberPagination(PageNumberPagination):
    # 默認每頁顯示的數據條數
    page_size = 2
    # 獲取URL參數中設置的每頁顯示數據條數
    page_size_query_param = 'size'
    # 獲取URL參數中傳入的頁碼key
    page_query_param = 'page'
    # 最大支持的每頁顯示的數據條數(對這個進行限制127.0.0.1:8000/books/?page=1&size=100)
    max_page_size = 3

class BookView(APIView):
    # authentication_classes = [TokenAuth, ]
    # parser_classes = [FormParser, JSONParser]
    pagination_class = MyPageNumberPagination    # 
    def get(self, request):
        book_list = Book.objects.all()   # queryset

        # 實例化分頁對象,獲取數據庫中的分頁數據
        pnp = MyPageNumberPagination()
        books_page = pnp.paginate_queryset(book_list, request, self)
        # 序列化對象
        bs = BookModelSerializers(books_page, many=True, context={"request": request})  # 序列化結果
        # return Response(bs.data)
        return bs.get_paginated_response(bs.data)  # 生成分頁和數據
    
    def post(self, request):.....
在視圖中使用pagination_class屬性調用該自定義類

 (1)設置max_page_size是限制直接在頁面訪問時最大的數據條數顯示

  

  雖然總共有4條數據,頁面訪問get請求時?page=1&size=100可是依然只能拿到max_page_size限制拿到的3條。

三、根據頁碼分頁——PageNumberPagination

  這個分頁樣式接受請求查詢參數中的一個數字頁面號。

GET https://api.example.org/accounts/?page=4

  響應對象:

HTTP 200 OK
{
    "count": 1023
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": [
       …
    ]
}

  繼承了APIView的視圖,也能夠設置pagination_class屬性選擇PageNumberPagination

class MyPageNumberPagination(PageNumberPagination):
    # 默認每頁顯示的數據條數
    page_size = 1

    # 獲取URL參數中設置的每頁顯示數據條數
    page_size_query_param = 'size'

    # 獲取URL參數中傳入的頁碼key
    page_query_param = 'page'

    # 最大支持的每頁顯示的數據條數(對這個進行限制127.0.0.1:8000/books/?page=1&size=100)
    max_page_size = 3

  更多配置屬性:

  • django_paginator_class  使用Django分頁類。默認爲django.core.paginator.Paginator,適用於大多數狀況
  • page_size  用來顯示每頁顯示對象的數量,若是設置了就重寫PAGE_SIZE設置。
  • page_query_param  頁面查詢參數,一個字符串值,指示用於分頁控件的查詢參數的名稱。
  • page_size_query_param  該參數容許客戶端根據每一個請求設置頁面大小。通常默認設置爲None.
  • max_page_size  只有設置了page_size_query_param參數,該參數纔有意義,爲客戶端請求頁面中可以顯示的最大數量
  • last_page_strings  用於存儲使用page_query_param參數請求過的值列表或元組,默認爲(‘last’,)
  • template  用來在可瀏覽API中,渲染分頁的模板(html)名字,能夠重寫分頁樣式,或者設置爲None,禁用分頁。默認爲」rest_framework/pagination/numbers.html」。

四、根據位置和個數分頁——LimitOffsetPagination

  這種分頁樣式與查找多個數據庫記錄時使用的語法相似。客戶端包括一個」limit」和一個 「offset」查詢參數。該限制表示返回的條目的最大數量,而且與page_size大小相同。偏移量表示查詢的起始位置,與完整的未分頁項的集合有關。

GET https://api.example.org/accounts/?limit=100&offset=400

HTTP 200 OK
{
    "count": 1023
    "next": "https://api.example.org/accounts/?limit=100&offset=500",
    "previous": "https://api.example.org/accounts/?limit=100&offset=300",
    "results": [
       …
    ]
}

  這種也能夠設置PAGE_SIZE,而後客戶端就能夠設置limit參數了。
  繼承了GenericAPIView的子類,能夠經過設置pagination_class屬性爲LimitOffsetPagination使用

class MyLimitOffsetPagination(LimitOffsetPagination):
    # 默認每頁顯示的數據條數
    default_limit = 1
    # URL中傳入的顯示數據條數的參數
    limit_query_param = 'limit'
    # URL中傳入的數據位置的參數
    offset_query_param = 'offset'
    # 最大每頁顯得條數
    max_limit = None

(重寫LimitOffsetPagination類)配置:

  • default_limit: 若是客戶端沒有提供,則默認使用與PAGE_SIZE值同樣。
  • limit_query_param:表示限制查詢參數的名字,默認爲’limit’
  • offset_query_param:表示偏移參數的名字, 默認爲’offset’
  • max_limit:容許頁面中顯示的最大數量,默認爲None
  • template: 渲染分頁結果的模板名,默認爲」rest_framework/pagination/numbers.html」.

五、遊標分頁——CursorPagination

  基於遊標的分頁顯示了一個不透明的「cursor」指示器,客戶端可使用它來瀏覽結果集。這種分頁方式只容許用戶向前或向後進行查詢。而且不容許客戶端導航到任意位置。

  基於遊標的分頁要求在結果集中有一個唯一的、不變的條目順序。這個排序一般是記錄上的一個建立時間戳,用來表示分頁的順序。

  基於遊標的分頁比其餘方案更復雜。它還要求結果集給出一個固定的順序,而且不容許客戶端任意地對結果集進行索引,可是它確實提供瞭如下好處:

  • 提供一致的分頁視圖。當使用正確的指針分頁時,即便在分頁過程當中其餘客戶端插入新項時,客戶端也不會在分頁時看到同一個項兩次。
  • 支持使用很是大的數據集。大量數據集使用基於off-set的分頁方式可能會變得低效或不可用。基於指針的分頁模式有固定的時間屬性,而且隨着數據集的大小的增長而不會減慢。
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class MyCursorPagination(CursorPagination):
    # URL傳入的遊標參數
    cursor_query_param = 'cursor'
    # 默認每頁顯示的數據條數
    page_size = 2
    # URL傳入的每頁顯示條數的參數
    page_size_query_param = 'page_size'
    # 每頁顯示數據最大條數
    max_page_size = 1000
    # 根據ID從大到小排列
    ordering = "id"

六、設置全局配置

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
    'PAGE_SIZE': 100
}

  顯示效果:

  

4、響應器(response)

 

5、渲染器

  規定頁面顯示的效果(無用)。

一、局部添加渲染器

  urls.py:

from django.conf.urls import url, include
from api.views import course

urlpatterns = [
    # path('admin/', admin.site.urls),
    url(r'^api/course/$', course.CourseView.as_view()),
]

  api/views/course.py:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer

class CourseView(APIView):

    # 渲染器
    # renderer_classes = [JSONRenderer]   # 表示只返回json格式
    renderer_classes = [JSONRenderer, BrowsableAPIRenderer]  # 數據嵌套在html中展現

    def get(self, request, *args, **kwargs):
        return Response('...')

  顯示效果:

  

二、全局添加渲染器

  settings.py:

# 渲染器配置到全局
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer']
}

  顯示效果同上。

6、版本 

一、DRF版本控制介紹

  API版本控制容許咱們在不一樣的客戶端之間更改行爲(同一個接口的不一樣版本會返回不一樣的數據)。

  DRF提供了許多不一樣的版本控制方案。可能會有一些客戶端由於某些緣由再也不維護了,可是咱們後端的接口還要不斷的更新迭代,這個時候經過版本控制返回不一樣的內容就是一種不錯的解決方案。

  DRF提供了五種版本控制方案以下所示:

from rest_framework import versioning           # view中引入版本控制

# 查看 rest_framework/versioning.py文件:
class BaseVersioning:...

class AcceptHeaderVersioning(BaseVersioning):   # 將版本信息放到請求頭中
    """
    GET /something/ HTTP/1.1
    Host: example.com
    Accept: application/json; version=1.0
    """

class URLPathVersioning(BaseVersioning):        # 將版本信息放入URL中
    """
    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 = [
        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
    """

class NamespaceVersioning(BaseVersioning):      # 經過namespace來區分版本
    """
    To the client this is the same style as `URLPathVersioning`.
    The difference is in the backend - this implementation uses
    Django's URL namespaces to determine the version.

    An example URL conf that is namespaced into two separate versions

    # 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'))
    ]

    GET /1.0/something/ HTTP/1.1
    Host: example.com
    Accept: application/json
    """

class HostNameVersioning(BaseVersioning):        # 經過主機名來區分版本
    """
    GET /something/ HTTP/1.1
    Host: v1.example.com
    Accept: application/json
    """

class QueryParameterVersioning(BaseVersioning):  # 經過url查詢參數區分版本
    """
    GET /something/?version=0.1 HTTP/1.1
    Host: example.com
    Accept: application/json
    """

 

一、源碼分析拿到版本信息

(1)查看APIView類中的dispatch方法

class APIView(View):

    def dispatch(self, request, *args, **kwargs):
        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)

(2)查看initial方法

class APIView(View):

    def initial(self, request, *args, **kwargs):

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

  能夠看到在這裏是有version的,也就是版本。

(3)繼續查看determine_version方法作了什麼

class APIView(View):

    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)

  1)繼續查看versioning_class

class APIView(View):

    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

  由此可知就是配置了一個類。所以在determine_version中self.versioning_class()是作了一個實例化的動做。

  2)return (scheme.determine_version(request, *args, **kwargs), scheme)返回了一個元組

  若是在/views/course.py的視圖類中定義versioning_class:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        self.dispatch
        return Response('...')

  則能夠實例化獲得scheme實例,並在函數返回語句中返回scheme。

  3)進一步查看QueryParameterVersioning中的determine_version

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

  這裏返回的是version,預計就是版本號。

  4)進一步查看request.query_params定義

class Request(object):

    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

  所以version = request.query_params.get(self.version_param, self.default_version)實際上是去URL中獲取GET傳來的version對應的參數

  5)查看version_param

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

  由此可知這是一個全局配置,默認值就等於version,由此可知前面返回的version就是版本號。

(4)version返回,分析inital方法

    def initial(self, request, *args, **kwargs):

        # Determine the API version, if versioning is in use.
        version, scheme = self.determine_version(request, *args, **kwargs)   
        request.version, request.versioning_scheme = version, scheme

  version是版本,scheme是對象並分別賦值給request.version和request.scheme。

(5)在視圖類中拿到版本

class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response('...')

(6)頁面訪問測試

  1)直接訪問

  

  此時python後臺輸出:none

  2)用url參數傳遞版本

  

  此時python後臺輸出:v1

 二、限制版本及源碼解析

 (1)查看QueryParameterVersioning類

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

  能夠看到version拿到後,用self.is_allowed_version方法作了一個判斷。

(2)查看is_allowed_version方法

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

  能夠看到ALLOWED_VERSIONS也是存放在配置文件中。

(3)在settings.py中添加容許的版本

# 渲染器配置到全局
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
                                 'rest_framework.renderers.BrowsableAPIRenderer'],
    'ALLOWED_VERSIONS': ['v1', 'v2']  # 容許的版本
}

(4)訪問驗證

  

三、默認版本與版本參數

  settings.py作以下配置

# 渲染器配置到全局
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
                                 'rest_framework.renderers.BrowsableAPIRenderer'],
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 容許的版本
    'VERSION_PARAM': 'version',     # 把默認的version修改成其餘參數:http://127.0.0.1:8000/api/course/?versionsss=v1
    'DEFAULT_VERSION': 'v1',        # 默認版本
}

(1)若是修改參數爲其餘值訪問效果以下

  

(2)配置默認版本後不寫版本參數也可獲取默認版本

  

  python後臺輸出:v1。

四、配置版本控制

(1)局部版本控制

  前面都是在單個類中配置了版本控制,以下所示:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning


class CourseView(APIView):
    versioning_class = QueryParameterVersioning

    def get(self, request, *args, **kwargs):
        self.dispatch
        return Response('...')

(2)全局版本控制

  源碼查看到全局版本控制配置信息:

class APIView(View):

    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    # Allow dependency injection of other settings to make testing easier.
    settings = api_settings

  所以也能夠在settings.py中配置全局版本控制:

# 渲染器配置到全局
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
                                 'rest_framework.renderers.BrowsableAPIRenderer'],
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 容許的版本
    'VERSION_PARAM': 'version',     # 把默認的version修改成其餘參數:http://127.0.0.1:8000/api/course/?versionsss=v1
    'DEFAULT_VERSION': 'v1',        # 默認版本
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning',  # 全局版本控制
}

  顯示效果以下:

  

五、寫版本推薦方式——基於url的正則方式(如:/v1/users/)

  前面寫的是基於url的get傳參方式,如:/users?version=v1,可是這種方式顯示版本不是最推薦的。通常須要把版本號寫在前面。改寫須要調整urls.py配置。

(1)項目urls.py修改以下:

from django.conf.urls import url, include

urlpatterns = [
    # path('admin/', admin.site.urls),
    url(r'^api/', include('api.urls')),
]

(2)應用目錄下建立urls.py文件,配置以下:

from django.conf.urls import url, include
from api.views import course

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()),
]

(3)修改/api/views/course.py類視圖文件以下所示:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning


class CourseView(APIView):
    versioning_class = URLPathVersioning

    def get(self, request, *args, **kwargs):
        print(request.version)
        return Response('...')

(4)訪問顯示效果

  

  之後都推薦用這種方式寫版本,全局配置修改同上。

六、其餘版本使用方式

  詳見:http://www.cnblogs.com/wupeiqi/articles/7805382.html

  基於 accept 請求頭方式,如:Accept: application/json; version=1.0

  基於主機名方法,如:v1.example.com

  基於django路由系統的namespace,如:example.com/v1/users/

相關文章
相關標籤/搜索