1. 程式人生 > >DAY102 - Rest Framework(七)- 手動編寫配置檔案、分頁器和版本控制

DAY102 - Rest Framework(七)- 手動編寫配置檔案、分頁器和版本控制

一、手動編寫配置檔案

# 預設配置
# conf-->global_setting.py
DEBUG = None

TIME_ZONE = 'America/Chicago'

USE_TZ = False


# 使用者配置
# usersetting-->setting.py
DEBUG = True

TIME_ZONE = 'Asia/Shanghai'

USE_TZ = False

ABC='123'
# conf-->__init__.py
from conf import global_setting
import os
import importlib


class MySetting():
    # dir:返回模組的屬性列表
    def __init__(self):

        settings_module = os.environ.get('USER_SETTING')
        # 通過環境變數,獲得使用者配置的檔案路徑(字串)
        # settings_module = 'usersetting.setting'
        
        # 迴圈獲得預設配置的值
        for setting in dir(global_setting):
            if setting.isupper():
                setattr(self, setting, getattr(global_setting, setting))
        
        # 使用importlib.import_module()獲得使用者配置的路徑
        mod = importlib.import_module(settings_module)
        
        # 迴圈獲得使用者配置的值,會覆蓋預設配置
        for setting in dir(mod):
            if setting.isupper():
                setattr(self, setting, getattr(mod, setting))


setting = MySetting()
# 執行檔案
import os

os.environ.setdefault('USER_SETTING', 'usersetting.setting')

from conf import setting
print(setting.DEBUG)

二、分頁器

1.普通分頁

基本使用

# 普通分頁
from rest_framework.pagination import PageNumberPagination

class Books(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        # 生成一個PageNumberPagination物件
        page = PageNumberPagination()
        # 第一個引數:要分頁的資料,第二個引數request物件,第三個引數,當前檢視物件
        # 在資料庫中獲取分頁的資料
        page_list = page.paginate_queryset(books, request, self)
        ret = BooksSerializer(instance=page_list, many=True)
        print(ret.data)
        return Response(ret.data)
    
        # 這個也是返回Response物件,但是比基本的多了上一頁,下一頁,和總資料條數(瞭解即可)
        # return page.get_paginated_response(ret.data)
# setting.py
# 配置每頁顯示數
REST_FRAMEWORK = {
    # 每頁顯示兩條
    'PAGE_SIZE': 2
}

配置屬性

# 方法一:自定義分頁類繼承PageNumberPagination

class MyPageNumberPagination(PageNumberPagination):
    # 每頁顯示的資料數,預設去setting裡找
    page_size = 3
    
    # 配置GET裡接收的key:value形式的key值,可以指定當前頁
    # http://127.0.0.1:8000/books/?page=2
    page_query_param = 'page'
    
    # 可以通過URL的方式指定每頁顯示的資料數
    # http://127.0.0.1:8000/books/?page=2&size=1
    page_size_query_param='size'
    
    # 每頁最多顯示資料數
    max_page_size = 4


class Books(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        # 生成一個自定義的物件
        page = MyPageNumberPagination()
        page_list = page.paginate_queryset(books, request, self)
        ret = BooksSerializer(instance=page_list, many=True)
        print(ret.data)
        return Response(ret.data)
# 方式二:視圖裡設定屬性
class Books(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        page = PageNumberPagination()
        page.page_size = 3
        page.page_query_param = 'page'
        page.page_size_query_param = 'size'
        page.max_page_size = 4
        page_list = page.paginate_queryset(books, request, self)
        ret = BooksSerializer(instance=page_list, many=True)
        print(ret.data)
        return Response(ret.data)

2.偏移分頁

基本使用

# 偏移分頁
from rest_framework.pagination import LimitOffsetPagination

class Books(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        # 生成一個LimitOffsetPagination物件
        page = LimitOffsetPagination()
        # 第一個引數:要分頁的資料,第二個引數request物件,第三個引數,當前檢視物件
        # 在資料庫中獲取分頁的資料
        page_list = page.paginate_queryset(books, request, self)
        ret = BooksSerializer(instance=page_list, many=True)
        print(ret.data)
        return Response(ret.data)
    
        # 這個也是返回Response物件,但是比基本的多了上一頁,下一頁,和總資料條數(瞭解即可)
        # return page.get_paginated_response(ret.data)
# setting.py
# 配置每頁顯示數
REST_FRAMEWORK = {
    # 每頁顯示兩條
    'default_limit':2
}

配置屬性

# 配置屬性的兩種方法與普通分頁一樣
# 每頁顯示的條數
default_limit = api_settings.PAGE_SIZE

# 標杆值
# 配置GET裡接收的key:value形式的key值,可以指定標杆值
# 一般和limit_query_param一起使用
offset_query_param = 'offset'

# 可以通過URL的方式指定每頁顯示的資料數
# http://127.0.0.1:8000/books/?offset=4&limit=3
# 從第四條資料(不包括)開始取三條資料
# 如果寫了limit=3,會覆蓋default_limit的資料數
limit_query_param = 'limit'

# 每頁顯示最大的條數
# 如果寫了limit的值>max_limit的值,以max_limit為準
max_limit = None

3.加密分頁

基本使用

# 加密分頁
from rest_framework.pagination import CursorPagination

class Books(APIView):
    def get(self, request):
        books = models.Book.objects.all()
        # 生成一個PageNumberPagination物件
        page = CursorPagination()
        # 先把資料按照ordering排序,再從資料庫取出來
        page.ordering = 'id'
        
        page_list = page.paginate_queryset(books, request, self)
        ret = BooksSerializer(instance=page_list, many=True)
        return Response(ret.data)
        # page.get_paginated_response()會返回連結和總的資料數
        # return page.get_paginated_response(ret.data)
# setting.py
# 配置每頁顯示數
REST_FRAMEWORK = {
    # 每頁顯示兩條
    'PAGE_SIZE': 2,
}

配置屬性

# 配置屬性的兩種方法與普通分頁一樣'
# 每頁顯示的條數
page_size = api_settings.PAGE_SIZE

# 在URL顯示加密後的頁碼
# http://127.0.0.1:8000/books/?cursor=cD0y
cursor_query_param = 'cursor

# 資料按照ordering排序,預設是'-created'
ordering = '-created'

# 每頁最多顯示資料數
max_page_size = 4

三、版本控制

基於restful規範,應當由版本之分,rest-framework給我們提供了一個

from rest_framework.versioning import QueryParameterVersioning,AcceptHeaderVersioning,NamespaceVersioning,URLPathVersioning

# 基於url的get傳參方式:
#  QueryParameterVersioning------>如:/users?version=v1

# 基於url的正則方式:
#  URLPathVersioning------>/v1/users/

# 基於 accept 請求頭方式:
#  AcceptHeaderVersioning------>Accept: application/json; version=1.0

# 基於主機名方法:
#  HostNameVersioning------>v1.example.com

# 基於django路由系統的namespace:
#  NamespaceVersioning------>example.com/v1/users/

基本使用

# 路由
url(r'^(?P<version>[v1|v2|v3]+)/books/', views.Books.as_view()),
from rest_framework.versioning import URLPathVersioning


class Books(APIView):
    # 區域性使用
    versioning_class = URLPathVersioning

    def get(self, request,*args,**kwargs):
        books = models.Book.objects.all()
        # 獲得版本號
        print(request.version)
        # URLPathVersioning物件
        print(request.versioning_scheme)
        
        ret = BooksSerializer(books, many=True)
        return Response(ret.data)
    
#全域性使用
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_VERSION': 'v1',            # 預設版本(從request物件裡取不到,顯示的預設值)
    'ALLOWED_VERSIONS': ['v1', 'v2'],   # 允許的版本
    'VERSION_PARAM': 'version'          # URL中獲取值的key
}

反向解析

# url
url(r'^(?P<version>[v1|v2|v3]+)/books/', views.Books.as_view(),name='test'),


# 反向生成URL
# 第一個引數是路由名,第二個是request
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url)
# http://127.0.0.1:8000/v1/books/

原始碼解析

# 第一步
# dispatch()
def dispatch(self, request, *args, **kwargs):
    
    ........
    # 由此進入下一步
    self.initial(request, *args, **kwargs)
    
    ........
    
# 第二步
# initial()
def initial(self, request, *args, **kwargs):

    ........
    # 把determine_version方法的返回值解壓賦值給version和scheme
    # 也就是version = 版本號
    # scheme = 版本類物件
    version, scheme = self.determine_version(request, *args, **kwargs)
    # 再把version和scheme分別賦值給request
    request.version, request.versioning_scheme = version, scheme
    
    ........
    
# 第三步
# self.determine_version()
def determine_version(self, request, *args, **kwargs):
    
    # 如果versioning_class is None,返回空元組
    if self.versioning_class is None:
        return (None, None)
    
    # 如果有,就加上()執行
    # 從視圖裡找,有:versioning_class = URLPathVersioning
    # scheme = URLPathVersioning()
    # 也就是說scheme是URLPathVersioning例項化後的物件
    scheme = self.versioning_class()
    
    # 返回元組(scheme.determine_version(request, *args, **kwargs),scheme)
    # scheme.determine_version(),是在URLPathVersioning裡的方法
    return (scheme.determine_version(request, *args, **kwargs), scheme)
# 第四步
# URLPathVersioning ---> determine_version
def determine_version(self, request, *args, **kwargs):
    # version_param和default_version都會從setting裡找
    # version_param='version'
    # default_version='v1'
    # 假設url: http://127.0.0.1:8000/v2/books/
    # 從引數裡找url傳進來的version,也就是v2,沒有就預設值self.default_version
    version = kwargs.get(self.version_param, self.default_version)
    
    # is_allowed_version,從下往上找,都沒有,在父類BaseVersioning裡
    # 也就是說,如果返回True,就直接把接收到的version返回,false則拋異常
    if not self.is_allowed_version(version):
        raise exceptions.NotFound(self.invalid_version_message)
    return version


# BaseVersioning ---> is_allowed_version()
def is_allowed_version(self, version):
    # 如果視圖裡沒有,會從setting裡找,還沒有,就去預設裡找,預設裡是None,就返回True
    # 也就是說,如果setting裡寫了但為空,或者沒寫,就返回True
    if not self.allowed_versions:
        return True
    
    # 只要version為空並且version不在allowed_versions範圍內
    # 或者version不等於預設值default_version並且version不在allowed_versions範圍內
    # 就會返回false
    return ((version is not None and version == self.default_version) or(version in self.allowed_versions))